예제 #1
0
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """

    def __init__(self, roamapp):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        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))

        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

        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.actionLegend.triggered.connect(self.updatelegend)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        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.bar)
        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)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.projectlabel = QLabel("Project: {project}")
        self.userlabel = QLabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = QLabel('')
        self.gpslabel = QLabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        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.statusbar.addPermanentWidget(self.scalebutton)

        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.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.spaceraction = self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.canvas.scaleChanged.connect(self.updatestatuslabel)

        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.showUIMessage)
        RoamEvents.selectionchanged.connect(self.showInfoResults)
        RoamEvents.show_widget.connect(self.dataentrywidget.add_widget)
        RoamEvents.closeProject.connect(self.close_project)

        GPS.gpsposition.connect(self.update_gps_label)
        GPS.gpsdisconnected.connect(self.gps_disconnected)

        self.legendpage.showmap.connect(self.showmap)

        self.currentselection = {}

    def update_scale_from_item(self, index):
        scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole))
        self.canvas.zoomScale(1.0 / scale)

    def selectscale(self):
        self.scalelist.show()

    def set_projectbuttons(self, visible):
        for action in self.projectbuttons:
            action.setVisible(visible)

    def loadpages(self, pages):
        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)

            iface = RoamInterface(RoamEvents, GPS, self)
            pagewidget = PageClass(iface, 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 showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''):
        self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra)

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def update_gps_label(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(gpsinfo.pdop,
                                                                        gpsinfo.hdop,
                                                                        gpsinfo.vdop))

    def gps_disconnected(self):
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        roam.api.utils.open_keyboard()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def delete_feature(self, form, feature):
        """
        Delete the selected feature
        """
        # We have to make the feature form because the user might have setup logic
        # to handle the delete case

        featureform = form.create_featureform(feature)
        try:
            msg = featureform.deletemessage
        except AttributeError:
            msg = 'Do you really want to delete this feature?'

        if not DeleteFeatureDialog(msg).exec_():
            return

        try:
            featureform.delete()
        except DeleteFeatureException as ex:
            RoamEvents.raisemessage(*ex.error)

        featureform.featuredeleted(feature)

    def search_for_projects(self):
        server = roam.config.settings.get('updateserver', '')
        print server
        self.projectupdater.update_server(server, self.projects)

    def settingsupdated(self, settings):
        self.settings = settings
        self.show()
        self.canvas_page.settings_updated(settings)

    def updatestatuslabel(self, *args):
        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 on_geometryedit(self, form, feature):
        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.iteritems():
            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)

    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):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8),
                                  info)



    def showhelp(self, parent, url):
        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 = QgsMapLayerRegistry.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 addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer

        if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]:
            geometry.convertToMultiType()

        try:
            # TODO: This is a gross hack. We need to move this out into a edit tool with better control.
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        feature = form.new_feature(set_defaults=True)
        feature.setGeometry(geometry)
        self.openForm(form, feature, editmode=False)

    def exit(self):
        """
        Exit the application.
        """
        self.close()

    def showInfoResults(self, results):
        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 missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        crs = self.canvas_page.init_qgisproject(doc)
        self.projectOpened()
        GPS.crs = crs

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        self.clear_plugins()
        self.add_plugins(self.project.enabled_plugins)

        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)

        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.set_projectbuttons(True)
        self.dataentrywidget.project = self.project
        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)
        RoamEvents.projectloaded.emit(self.project)

    def clear_plugins(self):
        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)
            widget.deleteLater()

            self.menutoolbar.removeAction(action)
        self.pluginactions = []

    def add_plugins(self, pluginnames):
        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.loadpages(pages)

    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.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.missingLayers)
        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def close_project(self, project=None):
        """
        Close the current open project
        """
        if not project is None and not project == self.project:
            return

        self.tracking.clear_logging()
        self.dataentrywidget.clear()
        self.canvas_page.cleanup()
        QgsMapLayerRegistry.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.set_projectbuttons(False)
        self.hidedataentry()
        self.infodock.close()
        RoamEvents.selectioncleared.emit()
        RoamEvents.projectClosed.emit(oldproject)
        self.projectwidget.set_open_project(None)
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.menutoolbar.setStyleSheet(roam.roam_style.menubarstyle)
        self.project = None
        self.tracking = GPSLogging(GPS)

        self.canvas_page.set_gps(GPS, self.tracking)

        self.canvas = self.canvas_page.canvas

        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.actionImport)
        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.actionHelp.triggered.connect(self.showhelp2)
        self.actionLegend.triggered.connect(self.updatelegend)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        self.gpswidget.setgps(GPS)
        self.gpswidget.settracking(self.tracking)

        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.bar)
        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)

        self.actionProject.triggered.connect(self.closeAnnotation)

        self.pHelp=os.getcwd()
        help_p = "help\IntraMaps Roam - Help.pdf"
        self.pathHelp=os.path.join(self.pHelp,help_p)
		
        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget



        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)


        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.gpslabel = createlabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.spaceraction = self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.annotationdock=AnnotationDock(self.canvas)
        self.annotationdock.hide()
        self.layerlist=LayerList(self.canvas)
        self.layerlist.hide()
        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.showUIMessage)
        RoamEvents.selectionchanged.connect(self.showInfoResults)
        RoamEvents.show_widget.connect(self.dataentrywidget.add_widget)
        RoamEvents.closeProject.connect(self.close_project)
        RoamEvents.showAnnotationUi.connect(self.showAnnotationDock)
        RoamEvents.showLayerListUi.connect(self.showLayerList)

        GPS.gpsposition.connect(self.update_gps_label)
        GPS.gpsdisconnected.connect(self.gps_disconnected)

        self.legendpage.showmap.connect(self.showmap)

        self.currentselection = {}

    def showAnnotationDock(self):
        self.annotationdock.show()

    def showLayerList(self,canvaslayers):
        self.layerlist.show(canvaslayers)

    def closeAnnotation(self):
        RoamEvents.closeAnnotation.emit()
        RoamEvents.DestroyLayerList.emit()

    def set_projectbuttons(self, visible):
        for action in self.projectbuttons:
            action.setVisible(visible)

    def loadpages(self, pages):
        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)

            iface = RoamInterface(RoamEvents, GPS, self)
            pagewidget = PageClass(iface, 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 showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''):
        self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra)

    def updatelegend(self):
        layers = self.project.legendlayersmapping().values()
        self.legendpage.show(layers)
        #self.legendpage.updatecanvas(self.canvas)

    def update_gps_label(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(gpsinfo.pdop,
                                                                        gpsinfo.hdop,
                                                                        gpsinfo.vdop))

    def gps_disconnected(self):
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        roam.api.utils.open_keyboard()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def settingsupdated(self, settings):
        self.show()
        self.canvas_page.settings_updated(settings)

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def on_geometryedit(self, form, feature):
        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.iteritems():
            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)

    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):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8),
                                  info)



    def showhelp(self, parent, url):
        help = HelpPage(parent)
        help.setHelpPage(url)
        help.show()

    def showhelp2(self):
		"""
        def showHTMLReport(title, html, data={}, parent=None):
            dialog = HtmlViewerDialog(title)
            dialog.showHTML(html, data)
            dialog.exec_()

        showHTMLReport("Help",contentHelp)"""
		QDesktopServices.openUrl(QUrl.fromLocalFile(self.pathHelp))

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuresdeleted(self, layerid, featureids):
        layer = QgsMapLayerRegistry.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 addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer

        if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]:
            geometry.convertToMultiType()

        try:
            # TODO: This is a gross hack. We need to move this out into a edit tool with better control.
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        feature = form.new_feature(set_defaults=True)
        feature.setGeometry(geometry)
        self.openForm(form, feature, editmode=False)

    def exit(self):
        """
        Exit the application.
        """
        self.close()

    def showInfoResults(self, results):
        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 missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        crs = self.canvas_page.init_qgisproject(doc)

        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        proj=str(self.project.folder)
        dbpath = proj + "/_data/annotation.sqlite"
        self.findAnnotationLayer(proj,dbpath)

        self.projectOpened()
        GPS.crs = crs

    def findAnnotationLayer(self, project_path, dbpath):
        flag=0
        if os.path.isfile(dbpath) == False:
            alert("DB Annotation non trovato, Annotation Tool disattivata")
            flag=1

        elif os.path.isfile(dbpath) == True:
            layerid=["point20150114085613922","line20150114085613640","polygon20150114085614076"]
            layer = QgsMapLayerRegistry.instance()

            for x in layerid:
                if layer.mapLayer(x)==None:
                    alert ("Uno o piu' layer Annotation non presente, Annotation Tool disattivata")
                    flag=1
                    break

        self.CheckAnno(flag)

    def CheckAnno(self,flag):
        if flag == 1:
            RoamEvents.setAnnoDisabled.emit()
        elif flag == 0:
            RoamEvents.setAnnoEnabled.emit()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))

        self.projectlabel.setText("Project: {}".format(self.project.name))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        self.clear_plugins()
        self.add_plugins(self.project.enabled_plugins)

        layers = self.project.legendlayersmapping().values()
        #self.legendpage.updateitems(layers)

        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.set_projectbuttons(True)
        RoamEvents.projectloaded.emit(self.project)

    def clear_plugins(self):
        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)
            widget.deleteLater()

            self.menutoolbar.removeAction(action)
        self.pluginactions = []

    def add_plugins(self, pluginnames):
        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.loadpages(pages)

    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.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.missingLayers)
        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def close_project(self, project=None):
        """
        Close the current open project
        """
        if not project is None and not project == self.project:
            return

        self.tracking.clear_logging()
        self.dataentrywidget.clear()
        self.canvas_page.cleanup()
        QgsMapLayerRegistry.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.set_projectbuttons(False)
        self.hidedataentry()
        self.infodock.close()
        RoamEvents.selectioncleared.emit()
        RoamEvents.projectClosed.emit(oldproject)
        self.projectwidget.set_open_project(None)

    def closeEvent(self, event):
        RoamEvents.DestroyLayerList.emit()
예제 #3
0
파일: mainwindow.py 프로젝트: GEO-IASS/Roam
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.canvaslayers = []
        self.layerbuttons = []
        self.project = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.bar = roam.messagebaritems.MessageBar(self.centralwidget)

        self.actionMap.setVisible(False)
        self.actionLegend.setVisible(False)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)

        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.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.actionLegend.triggered.connect(self.updatelegend)

        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self)
        self.projecttoolbar.addAction(self.actionGPS)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        self.gpswidget.setgps(GPS)

        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.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.gpslabel = createlabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.connectButtons()

        self.currentfeatureband = QgsRubberBand(self.canvas)
        self.currentfeatureband.setIconSize(20)
        self.currentfeatureband.setWidth(10)
        self.currentfeatureband.setColor(QColor(186, 93, 212, 76))

        self.canvas_page.layout().insertWidget(0, self.projecttoolbar)
        self.dataentryselection = QAction(self.projecttoolbar)
        self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection)
        self.dataentryselection.triggered.connect(self.selectdataentry)

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)

        RoamEvents.openimage.connect(self.openimage)
        RoamEvents.openurl.connect(self.viewurl)
        RoamEvents.openfeatureform.connect(self.openForm)
        RoamEvents.openkeyboard.connect(self.openkeyboard)
        RoamEvents.selectioncleared.connect(self.clearselection)
        RoamEvents.editgeometry.connect(self.addforedit)
        RoamEvents.editgeometry_complete.connect(self.on_geometryedit)
        RoamEvents.onShowMessage.connect(self.showUIMessage)
        RoamEvents.selectionchanged.connect(self.highlightselection)
        RoamEvents.selectionchanged.connect(self.showInfoResults)

        GPS.gpspostion.connect(self.updatecanvasfromgps)
        GPS.firstfix.connect(self.gpsfirstfix)
        GPS.gpsdisconnected.connect(self.gpsdisconnected)

        self.lastgpsposition = None
        self.marker = GPSMarker(self.canvas)
        self.marker.hide()

        self.legendpage.showmap.connect(self.showmap)

        self.editfeaturestack = []
        self.currentselection = {}

    def showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''):
        self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra)

    def addforedit(self, form, feature):
        self.editfeaturestack.append((form, feature))
        self.loadform(form)
        actions = self.getcaptureactions()
        for action in actions:
            if action.isdefault:
                action.trigger()
                break

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def gpsfirstfix(self, postion, gpsinfo):
        zoomtolocation = roam.config.settings.get('gpszoomonfix', True)
        if zoomtolocation:
            self.canvas.zoomScale(1000)

    def updatecanvasfromgps(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        if not self.lastgpsposition == position:
            self.lastposition = position
            rect = QgsRectangle(position, position)
            extentlimt = QgsRectangle(self.canvas.extent())
            extentlimt.scale(0.95)

            if not extentlimt.contains(position):
                self.canvas.setExtent(rect)
                self.canvas.refresh()

        self.marker.show()
        self.marker.setCenter(position)
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(gpsinfo.pdop,
                                                                        gpsinfo.hdop,
                                                                        gpsinfo.vdop))

    def gpsdisconnected(self):
        self.marker.hide()
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        if sys.platform == 'win32':
            try:
                programfiles = os.environ['ProgramW6432']
            except KeyError:
                programfiles = os.environ['ProgramFiles']

            cmd = r'{path}\Common Files\Microsoft Shared\ink\TabTip.exe'.format(path=programfiles)
            try:
                os.startfile(cmd)
            except WindowsError:
                roam.config.settings['keyboard'] = False
                roam.config.save()
        else:
            cmd = 'onboard'
            Popen(cmd)


    def selectdataentry(self):
        forms = self.project.forms
        formpicker = PickActionDialog(msg="Select data entry form")
        for form in forms:
            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(self.showformerror, form))
            else:
                action.triggered.connect(partial(self.loadform, form))
            formpicker.addAction(action)

        formpicker.exec_()

    def showformerror(self, form):
        pass

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def settingsupdated(self, settings):
        self.show()
        self.actionGPS.updateGPSPort()

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def on_geometryedit(self, form, feature):
        layer = form.QGISLayer
        self.reloadselection(layer, updated=[feature])

        self.currentfeatureband.setToGeometry(feature.geometry(), layer)

    def reloadselection(self, layer, deleted=[], updated=[]):
        """
        Reload the selection after features have been updated or deleted.
        :param layer:
        :param deleted:
        :param updated:
        :return:
        """
        selectedfeatures = self.currentselection[layer]

        # 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 highlightselection(self, results):
        self.clearselection()
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 200))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def clearselection(self):
        # Clear the main selection rubber band
        self.currentfeatureband.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

        self.editfeaturestack = []

    def highlightfeature(self, layer, feature, features):
        self.clearselection()
        self.highlightselection({layer: features})
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)

    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 dataentrychanged(self, index):
        self.clearCapatureTools()

        if not index.isValid():
            return

        modelindex = index
        # modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        self.dataentryselection.setCurrentIndex(index.row())
        self.createCaptureButtons(form)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8),
                                  info)

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def connectButtons(self):
        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 = PanTool(self.canvas)
        self.infoTool = InfoTool(self.canvas)

        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/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit)

        self.actionHome.triggered.connect(self.homeview)
        self.actionQuit.triggered.connect(self.exit)

    def getcaptureactions(self):
        for action in self.projecttoolbar.actions():
            if action.property('dataentry'):
                yield action

    def clearCapatureTools(self):
        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 createCaptureButtons(self, form):
        tool = form.getMaptool()(self.canvas)
        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.addNewFeature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showUIMessage, form.label))

    def loadform(self, form):
        self.clearCapatureTools()
        self.dataentryselection.setIcon(QIcon(form.icon))
        self.dataentryselection.setText(form.icontext)
        self.createCaptureButtons(form)

    def clearToolRubberBand(self):
        """
        Clear the rubber band of the active tool if it has one
        """
        tool = self.canvas.mapTool()
        try:
            tool.clearBand()
        except AttributeError:
            # No clearBand method found, but that's cool.
            pass

    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self, layer, featureid):
        self.dataentryfinished()
        self.reloadselection(layer, deleted=[featureid])
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.currentfeatureband.reset()
        self.clearToolRubberBand()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            RoamEvents.raisemessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def openForm(self, form, feature, editmode):
        """
        Open the form that is assigned to the layer
        """
        self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer)
        self.showdataentry()
        self.dataentrywidget.openform(feature=feature, form=form, project=self.project, editmode=editmode)

    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 addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer
        if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]:
            geometry.convertToMultiType()

        try:
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name() == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        self.openForm(form, feature, editmode=False)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        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.infodock.show()

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
            # Really!? We have to reload the whole layer set every time?
        # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0];
        #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0];
        #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0];
        #color = QColor(red, green, blue);
        #self.canvas.setCanvasColor(color)
        self.canvas.updateScale()
        self.projectOpened()
        self.canvas.freeze(False)
        self.canvas.refresh()
        GPS.crs = self.canvas.mapRenderer().destinationCrs()
        self.showmap()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))
        try:
            firstform = self.project.forms[0]
            self.loadform(self.project.forms[0])
            self.dataentryselection.setVisible(True)
        except IndexError:
            self.dataentryselection.setVisible(False)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        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()))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        self.infoTool.selectionlayers = self.project.selectlayersmapping()
        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)
        self.actionPan.trigger()

    #noinspection PyArgumentList
    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.WARNING, extrainfo=message)
            return

        self.actionMap.trigger()

        self.closeProject()

        self.canvas.refresh()
        self.canvas.repaint()

        RoamEvents.selectioncleared.emit()

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missingLayers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)

        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def closeProject(self):
        """
        Close the current open project
        """
        self.clearCapatureTools()
        self.canvas.freeze()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.clear()
        self.canvas.freeze(False)
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)

        self.panels = []
        self.project = None
        self.dataentrywidget.clear()
        self.hidedataentry()
        self.infodock.close()
예제 #4
0
파일: mainwindow.py 프로젝트: SFrav/Roam
class MainWindow(mainwindow_widget, mainwindow_base):
    """
    Main application window
    """

    def __init__(self, settings):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.settings = settings
        roam.featureform.settings = settings.settings
        self.canvaslayers = []
        self.layerbuttons = []
        self.project = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.actionMap.setVisible(False)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)
        self.menuGroup = QActionGroup(self)
        self.menuGroup.setExclusive(True)

        self.menuGroup.addAction(self.actionMap)
        self.menuGroup.addAction(self.actionDataEntry)
        self.menuGroup.addAction(self.actionProject)
        self.menuGroup.addAction(self.actionSync)
        self.menuGroup.addAction(self.actionSettings)
        self.menuGroup.triggered.connect(self.updatePage)

        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)

        #TODO Extract GPS out into a service and remove UI stuff
        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self.settings, self)
        self.projecttoolbar.addAction(self.actionGPS)

        self.projectwidget = ProjectsWidget(self)
        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)
        self.project_page.layout().addWidget(self.projectwidget)

        self.syncwidget = SyncWidget()
        self.syncpage.layout().addWidget(self.syncwidget)

        self.settingswidget = SettingsWidget(settings, self)
        self.settings_page.layout().addWidget(self.settingswidget)
        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.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)
        self.dataentrywidget.openimage.connect(self.openimage)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)
        self.stackedWidget.currentChanged.connect(self.updateUIState)

        self.panels = []

        self.connectButtons()

        self.band = QgsRubberBand(self.canvas)
        self.band.setIconSize(20)
        self.band.setWidth(10)
        self.band.setColor(QColor(186, 93, 212, 76))

        self.canvas_page.layout().insertWidget(0, self.projecttoolbar)
        self.dataentrymodel = QStandardItemModel(self)
        self.dataentrycombo = QComboBox(self.projecttoolbar)
        self.dataentrycombo.setIconSize(QSize(48,48))
        self.dataentrycombo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.dataentrycombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.dataentrycombo.setModel(self.dataentrymodel)
        self.dataentrycomboaction = self.projecttoolbar.insertWidget(self.topspaceraction, self.dataentrycombo)

        self.dataentrycombo.showPopup = self.selectdataentry

        self.biglist = BigList(self.canvas)
        self.biglist.setlabel("Select data entry form")
        self.biglist.setmodel(self.dataentrymodel)
        self.biglist.itemselected.connect(self.dataentrychanged)
        self.biglist.hide()

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.requestopenform.connect(self.openForm)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.resultscleared.connect(self.clearselection)
        self.infodock.openurl.connect(self.viewurl)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.projecttoolbar.toolButtonStyleChanged.connect(self.updatecombo)

    def selectdataentry(self, ):
        if self.dataentrycombo.count() == 0:
            return

        self.biglist.show()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :return:
        """
        key = url.toString().lstrip('file://')
        try:
            # Hack. Eww fix me.
            data, imagetype = roam.htmlviewer.images[os.path.basename(key)]
        except KeyError:
            # It's not a image so lets just pass it of as a normal
            # URL
            QDesktopServices.openUrl(url)
            return

        pix = QPixmap()
        if imagetype == 'base64':
            pix.loadFromData(data)
        else:
            pix.load(data)

        self.openimage(pix)

    def openimage(self, pixmap):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def updatecombo(self, *args):
        self.dataentrycombo.setMinimumHeight(0)

    def settingsupdated(self, settings):
        settings.save()
        self.show()
        self.actionGPS.updateGPSPort()
        # eww!
        roam.featureform.settings = settings.settings

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def highlightselection(self, results):
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 150))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def clearselection(self):
        # Clear the main selection rubber band
        self.band.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

    def highlightfeature(self, layer, feature, features):
        self.clearselection()
        self.highlightselection({layer: features})
        self.band.setToGeometry(feature.geometry(), layer)

    def showmap(self):
        self.actionMap.setVisible(True)
        self.actionMap.trigger()

    def hidedataentry(self):
        self.actionDataEntry.setVisible(False)

    def showdataentry(self):
        self.actionDataEntry.setVisible(True)
        self.actionDataEntry.trigger()

    def dataentrychanged(self, index):
        wasactive = self.clearCapatureTools()

        if not index.isValid():
            return

        modelindex = index
        # modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        self.dataentrycombo.setCurrentIndex(index.row())
        self.createCaptureButtons(form, wasactive)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError('Seems something has gone wrong. Press for more details',
                                  info)

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def connectButtons(self):
        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 = TouchMapTool(self.canvas)
        self.moveTool = MoveTool(self.canvas, [])
        self.infoTool = InfoTool(self.canvas)

        connectAction(self.actionZoom_In, self.zoomInTool)
        connectAction(self.actionZoom_Out, self.zoomOutTool)
        connectAction(self.actionPan, self.panTool)
        connectAction(self.actionMove, self.moveTool)
        connectAction(self.actionInfo, self.infoTool)

        self.zoomInTool.setCursor(cursor(':/icons/in'))
        self.zoomOutTool.setCursor(cursor(':/icons/out'))
        self.infoTool.setCursor(cursor(':/icons/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(self.showInfoResults)

        # The edit toolbutton is currently not being used but leaving it for feature.
        self.moveTool.layersupdated.connect(self.actionMove.setEnabled)
        self.moveTool.layersupdated.connect(self.actionEdit_Tools.setEnabled)

        self.actionGPSFeature.triggered.connect(self.addFeatureAtGPS)
        self.actionGPSFeature.setEnabled(self.actionGPS.isConnected)
        self.actionGPS.gpsfixed.connect(self.actionGPSFeature.setEnabled)

        self.actionHome.triggered.connect(self.homeview)
        self.actionQuit.triggered.connect(self.exit)

    def showToolError(self, label, message):
        self.bar.pushMessage(label, message, QgsMessageBar.WARNING)

    def clearCapatureTools(self):
        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 createCaptureButtons(self, form, wasselected):
        tool = form.getMaptool()(self.canvas)
        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)

            if action.isdefault:
                action.setChecked(wasselected)

        if hasattr(tool, 'geometryComplete'):
            add = partial(self.addNewFeature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showToolError, form.label))

        self.projecttoolbar.insertAction(self.topspaceraction, self.actionGPSFeature)
        self.actionGPSFeature.setVisible(not tool.isEditTool())

    def createFormButtons(self, forms):
        """
            Create buttons for each form that is defined
        """
        self.dataentrymodel.clear()
        self.clearCapatureTools()


        def captureFeature(form):
            item = QStandardItem(QIcon(form.icon), form.icontext)
            item.setData(form, Qt.UserRole + 1)
            item.setSizeHint(QSize(item.sizeHint().width(), self.projecttoolbar.height()))
            self.dataentrymodel.appendRow(item)

        capabilitityhandlers = {"capture": captureFeature}

        failedforms = []
        for form in forms:
            valid, reasons = form.valid
            if not valid:
                roam.utils.log("Form is invalid for data entry because {}".format(reasons))
                failedforms.append((form, reasons))
                continue

            for capability in form.capabilities:
                try:
                    capabilitityhandlers[capability](form)
                except KeyError:
                    # Just ignore capabilities we don't support yet.
                    continue

        if failedforms:
            for form, reasons in failedforms:
                html = "<h3>{}</h3><br>{}".format(form.label,
                                             "<br>".join(reasons))
            self.bar.pushMessage("Form errors",
                                 "Looks like some forms couldn't be loaded",
                                 level=QgsMessageBar.WARNING, extrainfo=html)

        visible = self.dataentrymodel.rowCount() > 0
        self.dataentrycomboaction.setVisible(visible)
        self.dataentrycombo.setMinimumHeight(self.projecttoolbar.height())

        index = self.dataentrymodel.index(0, 0)
        self.dataentrychanged(index)

    def addFeatureAtGPS(self):
        """
        Add a record at the current GPS location.
        """
        index = self.dataentrycombo.currentIndex()
        modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        point = self.actionGPS.position
        point = QgsGeometry.fromPoint(point)
        self.addNewFeature(form=form, geometry=point)

    def clearToolRubberBand(self):
        """
        Clear the rubber band of the active tool if it has one
        """
        tool = self.canvas.mapTool()
        try:
            tool.clearBand()
        except AttributeError:
            # No clearBand method found, but that's cool.
            pass

    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self):
        self.dataentryfinished()
        self.bar.pushMessage("Deleted", "Feature Deleted", QgsMessageBar.INFO, 1)
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.band.reset()
        self.clearToolRubberBand()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            self.bar.pushMessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def openForm(self, form, feature):
        """
        Open the form that is assigned to the layer
        """
        self.band.setToGeometry(feature.geometry(), form.QGISLayer)
        self.showdataentry()
        self.dataentrywidget.openform(feature=feature, form=form, project=self.project)

    def addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name() == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        self.openForm(form, feature)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        self.infodock.clearResults()
        forms = {}
        for layer in results.keys():
            layername = layer.name()
            if not layername in forms:
                forms[layername] = list(self.project.formsforlayer(layername))

        self.infodock.setResults(results, forms)
        self.infodock.show()

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
            # Really!? We have to reload the whole layer set every time?
        # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 = self.settings.settings.get("fullscreen", False)
        if fullscreen:
            self.showFullScreen()
        else:
            self.showMaximized()

    def viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    def updateUIState(self, page):
        """
        Update the UI state to reflect the currently selected
        page in the stacked widget
        """
        pass

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.projectOpened()
        self.canvas.freeze(False)
        self.canvas.refresh()
        self.showmap()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))
        self.createFormButtons(forms=self.project.forms)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        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()))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        # TODO Abstract this out
        if not self.project.selectlayers:
            selectionlayers = QgsMapLayerRegistry.instance().mapLayers().values()
        else:
            selectionlayers = []
            for layername in self.project.selectlayers:
                try:
                    layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0]
                except IndexError:
                    roam.utils.warning("Can't find QGIS layer for select layer {}".format(layername))
                    continue
                selectionlayers.append(layer)

        self.infoTool.selectionlayers = selectionlayers
        self.actionPan.trigger()

    #noinspection PyArgumentList
    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.WARNING, extrainfo=message)
            return

        self.actionMap.trigger()

        self.closeProject()

        self.canvas.refresh()
        self.canvas.repaint()

        self.infodock.clearResults()

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missingLayers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)

        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def closeProject(self):
        """
        Close the current open project
        """
        self.canvas.freeze()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.clear()
        self.canvas.freeze(False)
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)

        self.dataentrymodel.clear()
        self.panels = []
        self.project = None
        self.dataentrywidget.clear()
        self.hidedataentry()
        self.infodock.close()
예제 #5
0
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)
예제 #6
0
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.menutoolbar.setStyleSheet(roam.roam_style.menubarstyle)
        self.project = None
        self.tracking = GPSLogging(GPS)

        self.canvas_page.set_gps(GPS, self.tracking)

        self.canvas = self.canvas_page.canvas

        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.actionQuit.triggered.connect(self.exit)
        self.actionLegend.triggered.connect(self.updatelegend)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        self.gpswidget.setgps(GPS)
        self.gpswidget.settracking(self.tracking)

        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.bar)
        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)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel(
            "User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.gpslabel = createlabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)

        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.showUIMessage)
        RoamEvents.selectionchanged.connect(self.showInfoResults)
        RoamEvents.show_widget.connect(self.dataentrywidget.add_widget)

        GPS.gpsposition.connect(self.update_gps_label)
        GPS.gpsdisconnected.connect(self.gps_disconnected)

        self.legendpage.showmap.connect(self.showmap)

        self.currentselection = {}

    def showUIMessage(self,
                      label,
                      message,
                      level=QgsMessageBar.INFO,
                      time=0,
                      extra=''):
        self.bar.pushMessage(label,
                             message,
                             level,
                             duration=time,
                             extrainfo=extra)
        roam.utils.info(label)
        roam.utils.info(message)
        roam.utils.info(extra)

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def update_gps_label(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(
            gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop))

    def gps_disconnected(self):
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        roam.api.utils.open_keyboard()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def settingsupdated(self, settings):
        self.show()
        self.canvas_page.settings_updated(settings)

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(
            extent.center().toString()))

    def on_geometryedit(self, form, feature):
        layer = form.QGISLayer
        self.reloadselection(layer, updated=[feature])

    def reloadselection(self, layer, deleted=[], updated=[]):
        """
        Reload the selection after features have been updated or deleted.
        :param layer:
        :param deleted:
        :param updated:
        :return:
        """
        selectedfeatures = self.currentselection[layer]

        # 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)

    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):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(
            QApplication.translate(
                'MainWindowPy',
                'Seems something has gone wrong. Press for more details', None,
                QApplication.UnicodeUTF8), info)

    def showhelp(self, parent, url):
        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 = QgsMapLayerRegistry.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 addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer

        if layer.geometryType() in [
                QGis.WKBMultiLineString, QGis.WKBMultiPoint,
                QGis.WKBMultiPolygon
        ]:
            geometry.convertToMultiType()

        try:
            # TODO: This is a gross hack. We need to move this out into a edit tool with better control.
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        feature = form.new_feature(set_defaults=True)
        feature.setGeometry(geometry)
        self.openForm(form, feature, editmode=False)

    def exit(self):
        """
        Exit the application.
        """
        self.close()

    def showInfoResults(self, results):
        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 missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        crs = self.canvas_page.init_qgisproject(doc)
        self.projectOpened()
        GPS.crs = crs

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)

        try:
            gps_loglayer = QgsMapLayerRegistry.instance().mapLayersByName(
                'gps_log')[0]
            if roam.config.settings.get('gpslogging', True):
                self.tracking.enable_logging_on(gps_loglayer)
        except IndexError:
            roam.utils.info("No gps_log found for GPS logging")
            self.tracking.clear_logging()

        self.canvas_page.project_loaded(self.project)
        self.showmap()

    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.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.missingLayers)
        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def close_project(self):
        """
        Close the current open project
        """
        self.tracking.clear_logging()
        self.dataentrywidget.clear()
        self.canvas_page.cleanup()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons

        self.panels = []
        self.project = None
        self.hidedataentry()
        self.infodock.close()
        RoamEvents.selectioncleared.emit()
예제 #7
0
파일: mainwindow.py 프로젝트: nicklv/Roam
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """

    def __init__(self, roamapp):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        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))

        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.actionLegend.triggered.connect(self.updatelegend)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        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)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        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.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.showUIMessage)
        RoamEvents.selectionchanged.connect(self.showInfoResults)
        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)
        plugins.api = iface

    def set_projectbuttons(self, visible):
        for action in self.projectbuttons:
            action.setVisible(visible)

    def loadpages(self, pages):
        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 showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''):
        self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra)

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        roam.api.utils.open_keyboard()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def delete_featue(self, form, feature):
        """
        Delete the selected feature
        """
        # We have to make the feature form because the user might have setup logic
        # to handle the delete case

        featureform = form.create_featureform(feature)
        try:
            msg = featureform.deletemessage
        except AttributeError:
            msg = 'Do you really want to delete this feature?'

        if not DeleteFeatureDialog(msg).exec_():
            return

        try:
            featureform.delete()
        except DeleteFeatureException as ex:
            RoamEvents.raisemessage(*ex.error)

        featureform.featuredeleted(feature)

        # TODO Fix undo delete stuff
        # self.show_undo("Feature deleted", "Undo Delete", form, feature)

    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):
        # 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):
        server = roam.config.settings.get('updateserver', '')
        self.projectupdater.update_server(server, self.projects)

    def settingsupdated(self, settings):
        self.settings = settings
        self.show()
        self.canvas_page.settings_updated(settings)

    def on_geometryedit(self, form, feature):
        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.iteritems():
            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):
        import errors
        info = self.bar.pushError(*exinfo)
        errors.send_exception(exinfo)

    def showhelp(self, parent, url):
        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 = QgsMapLayerRegistry.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):
        """
        Exit the application.
        """
        self.projectupdater.quit()
        self.close()

    def showInfoResults(self, results):
        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 missingLayers(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 loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        crs = self.canvas_page.init_qgisproject(doc)
        self.projectOpened()
        GPS.crs = crs
        text = "This is a extra bit of info \n but just as a notice"

    @property
    def enabled_plugins(self):
        return self.settings.get('plugins', [])

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        self.clear_plugins()
        self.add_plugins(self.project.enabled_plugins)

        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)

        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.set_projectbuttons(True)
        self.dataentrywidget.project = self.project
        RoamEvents.projectloaded.emit(self.project)

    def clear_plugins(self):
        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):
        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.loadpages(pages)

    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.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.missingLayers)
        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def close_project(self, project=None):
        """
        Close the current open project
        """
        if not project is None and not project == self.project:
            return

        RoamEvents.projectClosing.emit()
        self.tracking.clear_logging()
        self.dataentrywidget.clear()
        self.canvas_page.cleanup()
        QgsMapLayerRegistry.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.set_projectbuttons(False)
        self.hidedataentry()
        self.infodock.close()
        RoamEvents.selectioncleared.emit()
        RoamEvents.projectClosed.emit(oldproject)
        self.projectwidget.set_open_project(None)
예제 #8
0
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.canvaslayers = []
        self.layerbuttons = []
        self.project = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.bar = roam.messagebaritems.MessageBar(self.centralwidget)

        self.actionMap.setVisible(False)
        self.actionLegend.setVisible(False)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)

        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.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.actionLegend.triggered.connect(self.updatelegend)

        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self)
        self.projecttoolbar.addAction(self.actionGPS)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        self.gpswidget.setgps(GPS)

        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.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding,
                                     QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(
            self.actionGPS, gpsspacewidget)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel(
            "User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.gpslabel = createlabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.connectButtons()

        self.currentfeatureband = QgsRubberBand(self.canvas)
        self.currentfeatureband.setIconSize(20)
        self.currentfeatureband.setWidth(10)
        self.currentfeatureband.setColor(QColor(186, 93, 212, 76))

        self.canvas_page.layout().insertWidget(0, self.projecttoolbar)
        self.dataentryselection = QAction(self.projecttoolbar)
        self.dataentryaction = self.projecttoolbar.insertAction(
            self.topspaceraction, self.dataentryselection)
        self.dataentryselection.triggered.connect(self.selectdataentry)

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)

        RoamEvents.openimage.connect(self.openimage)
        RoamEvents.openurl.connect(self.viewurl)
        RoamEvents.openfeatureform.connect(self.openForm)
        RoamEvents.openkeyboard.connect(self.openkeyboard)
        RoamEvents.selectioncleared.connect(self.clearselection)
        RoamEvents.editgeometry.connect(self.addforedit)
        RoamEvents.editgeometry_complete.connect(self.on_geometryedit)
        RoamEvents.onShowMessage.connect(self.showUIMessage)
        RoamEvents.selectionchanged.connect(self.highlightselection)
        RoamEvents.selectionchanged.connect(self.showInfoResults)

        GPS.gpspostion.connect(self.updatecanvasfromgps)
        GPS.firstfix.connect(self.gpsfirstfix)
        GPS.gpsdisconnected.connect(self.gpsdisconnected)

        self.lastgpsposition = None
        self.marker = GPSMarker(self.canvas)
        self.marker.hide()

        self.legendpage.showmap.connect(self.showmap)

        self.editfeaturestack = []
        self.currentselection = {}

    def showUIMessage(self,
                      label,
                      message,
                      level=QgsMessageBar.INFO,
                      time=0,
                      extra=''):
        self.bar.pushMessage(label,
                             message,
                             level,
                             duration=time,
                             extrainfo=extra)

    def addforedit(self, form, feature):
        self.editfeaturestack.append((form, feature))
        self.loadform(form)
        actions = self.getcaptureactions()
        for action in actions:
            if action.isdefault:
                action.trigger()
                break

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def gpsfirstfix(self, postion, gpsinfo):
        zoomtolocation = roam.config.settings.get('gpszoomonfix', True)
        if zoomtolocation:
            self.canvas.zoomScale(1000)

    def updatecanvasfromgps(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        if not self.lastgpsposition == position:
            self.lastposition = position
            rect = QgsRectangle(position, position)
            extentlimt = QgsRectangle(self.canvas.extent())
            extentlimt.scale(0.95)

            if not extentlimt.contains(position):
                self.canvas.setExtent(rect)
                self.canvas.refresh()

        self.marker.show()
        self.marker.setCenter(position)
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(
            gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop))

    def gpsdisconnected(self):
        self.marker.hide()
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        if sys.platform == 'win32':
            try:
                programfiles = os.environ['ProgramW6432']
            except KeyError:
                programfiles = os.environ['ProgramFiles']

            cmd = r'{path}\Common Files\Microsoft Shared\ink\TabTip.exe'.format(
                path=programfiles)
            try:
                os.startfile(cmd)
            except WindowsError:
                roam.config.settings['keyboard'] = False
                roam.config.save()
        else:
            cmd = 'onboard'
            Popen(cmd)

    def selectdataentry(self):
        forms = self.project.forms
        formpicker = PickActionDialog(msg="Select data entry form")
        for form in forms:
            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(self.showformerror, form))
            else:
                action.triggered.connect(partial(self.loadform, form))
            formpicker.addAction(action)

        formpicker.exec_()

    def showformerror(self, form):
        pass

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def settingsupdated(self, settings):
        self.show()
        self.actionGPS.updateGPSPort()

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(
            extent.center().toString()))

    def on_geometryedit(self, form, feature):
        layer = form.QGISLayer
        self.reloadselection(layer, updated=[feature])

        self.currentfeatureband.setToGeometry(feature.geometry(), layer)

    def reloadselection(self, layer, deleted=[], updated=[]):
        """
        Reload the selection after features have been updated or deleted.
        :param layer:
        :param deleted:
        :param updated:
        :return:
        """
        selectedfeatures = self.currentselection[layer]

        # 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 highlightselection(self, results):
        self.clearselection()
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 200))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def clearselection(self):
        # Clear the main selection rubber band
        self.currentfeatureband.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

        self.editfeaturestack = []

    def highlightfeature(self, layer, feature, features):
        self.clearselection()
        self.highlightselection({layer: features})
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)

    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 dataentrychanged(self, index):
        self.clearCapatureTools()

        if not index.isValid():
            return

        modelindex = index
        # modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        self.dataentryselection.setCurrentIndex(index.row())
        self.createCaptureButtons(form)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(
            QApplication.translate(
                'MainWindowPy',
                'Seems something has gone wrong. Press for more details', None,
                QApplication.UnicodeUTF8), info)

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def connectButtons(self):
        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 = PanTool(self.canvas)
        self.infoTool = InfoTool(self.canvas)

        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/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit)

        self.actionHome.triggered.connect(self.homeview)
        self.actionQuit.triggered.connect(self.exit)

    def getcaptureactions(self):
        for action in self.projecttoolbar.actions():
            if action.property('dataentry'):
                yield action

    def clearCapatureTools(self):
        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 createCaptureButtons(self, form):
        tool = form.getMaptool()(self.canvas)
        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.addNewFeature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showUIMessage, form.label))

    def loadform(self, form):
        self.clearCapatureTools()
        self.dataentryselection.setIcon(QIcon(form.icon))
        self.dataentryselection.setText(form.icontext)
        self.createCaptureButtons(form)

    def clearToolRubberBand(self):
        """
        Clear the rubber band of the active tool if it has one
        """
        tool = self.canvas.mapTool()
        try:
            tool.clearBand()
        except AttributeError:
            # No clearBand method found, but that's cool.
            pass

    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self, layer, featureid):
        self.dataentryfinished()
        self.reloadselection(layer, deleted=[featureid])
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.currentfeatureband.reset()
        self.clearToolRubberBand()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            RoamEvents.raisemessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def openForm(self, form, feature, editmode):
        """
        Open the form that is assigned to the layer
        """
        self.currentfeatureband.setToGeometry(feature.geometry(),
                                              form.QGISLayer)
        self.showdataentry()
        self.dataentrywidget.openform(feature=feature,
                                      form=form,
                                      project=self.project,
                                      editmode=editmode)

    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 addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer
        if layer.geometryType() in [
                QGis.WKBMultiLineString, QGis.WKBMultiPoint,
                QGis.WKBMultiPolygon
        ]:
            geometry.convertToMultiType()

        try:
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name(
            ) == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        self.openForm(form, feature, editmode=False)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        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.infodock.show()

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
            # Really!? We have to reload the whole layer set every time?
        # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0];
        #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0];
        #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0];
        #color = QColor(red, green, blue);
        #self.canvas.setCanvasColor(color)
        self.canvas.updateScale()
        self.projectOpened()
        self.canvas.freeze(False)
        self.canvas.refresh()
        GPS.crs = self.canvas.mapRenderer().destinationCrs()
        self.showmap()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))
        try:
            firstform = self.project.forms[0]
            self.loadform(self.project.forms[0])
            self.dataentryselection.setVisible(True)
        except IndexError:
            self.dataentryselection.setVisible(False)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        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()))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        self.infoTool.selectionlayers = self.project.selectlayersmapping()
        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)
        self.actionPan.trigger()

    #noinspection PyArgumentList
    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.WARNING,
                                 extrainfo=message)
            return

        self.actionMap.trigger()

        self.closeProject()

        self.canvas.refresh()
        self.canvas.repaint()

        RoamEvents.selectioncleared.emit()

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missingLayers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)

        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def closeProject(self):
        """
        Close the current open project
        """
        self.clearCapatureTools()
        self.canvas.freeze()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.clear()
        self.canvas.freeze(False)
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)

        self.panels = []
        self.project = None
        self.dataentrywidget.clear()
        self.hidedataentry()
        self.infodock.close()
예제 #9
0
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow):
    """
    Main application window
    """

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.project = None
        self.tracking = GPSLogging(GPS)

        self.canvas_page.set_gps(GPS, self.tracking)

        self.canvas = self.canvas_page.canvas

        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.actionQuit.triggered.connect(self.exit)
        self.actionLegend.triggered.connect(self.updatelegend)

        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)

        self.gpswidget.setgps(GPS)
        self.gpswidget.settracking(self.tracking)

        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.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.gpslabel = createlabel("GPS: Not active")
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer2 = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)

        self.panels = []

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)

        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.showUIMessage)
        RoamEvents.selectionchanged.connect(self.showInfoResults)
        RoamEvents.featureformloaded.connect(self.featureformloaded)

        GPS.gpsposition.connect(self.update_gps_label)
        GPS.gpsdisconnected.connect(self.gps_disconnected)

        self.legendpage.showmap.connect(self.showmap)

        self.currentselection = {}

    def showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''):
        self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra)

    def updatelegend(self):
        self.legendpage.updatecanvas(self.canvas)

    def update_gps_label(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        self.gpslabel.setText("GPS: PDOP {}   HDOP {}    VDOP {}".format(gpsinfo.pdop,
                                                                        gpsinfo.hdop,
                                                                        gpsinfo.vdop))

    def gps_disconnected(self):
        self.gpslabel.setText("GPS Not Active")

    def openkeyboard(self):
        if not roam.config.settings.get('keyboard', True):
            return

        roam.api.utils.open_keyboard()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :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):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def settingsupdated(self, settings):
        self.show()
        self.canvas_page.settings_updated(settings)

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def on_geometryedit(self, form, feature):
        layer = form.QGISLayer
        self.reloadselection(layer, updated=[feature])

    def reloadselection(self, layer, deleted=[], updated=[]):
        """
        Reload the selection after features have been updated or deleted.
        :param layer:
        :param deleted:
        :param updated:
        :return:
        """
        selectedfeatures = self.currentselection[layer]

        # 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)

    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):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8),
                                  info)



    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self, layer, featureid):
        self.dataentryfinished()
        self.reloadselection(layer, deleted=[featureid])
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.canvas_page.clear_temp_objects()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            RoamEvents.raisemessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def featureformloaded(self, form, feature, project, editmode):
        self.showdataentry()

    def openForm(self, form, feature, editmode):
        """
        Open the form that is assigned to the layer
        """
        self.dataentrywidget.openform(feature=feature, form=form, project=self.project, editmode=editmode)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        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.infodock.show()


    def missingLayers(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,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(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 viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        crs = self.canvas_page.init_qgisproject(doc)
        self.projectOpened()
        GPS.crs = crs

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        layers = self.project.legendlayersmapping().values()
        self.legendpage.updateitems(layers)

        try:
            gps_loglayer = QgsMapLayerRegistry.instance().mapLayersByName('gps_log')[0]
            if roam.config.settings.get('gpslogging', True):
                self.tracking.enable_logging_on(gps_loglayer)
        except IndexError:
            roam.utils.info("No gps_log found for GPS logging")
            self.tracking.clear_logging()

        self.canvas_page.project_loaded(self.project)
        self.showmap()

    @roam.utils.timeit
    def loadProject(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.",
                                 QgsMessageBar.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.missingLayers)
        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))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def close_project(self):
        """
        Close the current open project
        """
        self.tracking.clear_logging()
        self.dataentrywidget.clear()
        self.canvas_page.cleanup()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons

        self.panels = []
        self.project = None
        self.hidedataentry()
        self.infodock.close()
        RoamEvents.selectioncleared.emit()