Example #1
0
    def load_feature_form(self,
                          feature,
                          form,
                          editmode,
                          clear=True,
                          callback=None):
        """
        Opens the form for the given feature.
        """
        def _sink(_):
            pass

        if not callback:
            callback = _sink

        if clear:
            # Clear all the other open widgets that might be open.
            self.clear(dontemit=True)

        # One capture geometry, even for sub forms?
        # HACK Remove me and do something smarter
        roam.qgisfunctions.capturegeometry = feature.geometry()

        # Hold a reference to the fields because QGIS will let the
        # go out of scope and we get crashes. Yay!
        layer = form.QGISLayer
        self.fields = feature.fields()
        attributes = feature.attributes()

        fields = [field.name().lower() for field in self.fields]
        # Something is strange with default values and spatilite. Just delete them for now.
        if layer.dataProvider().name() == 'spatialite':
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            for index in pkindexes:
                del fields[index]
                del attributes[index]
        values = CaseInsensitiveDict(zip(fields, attributes))

        defaultvalues = {}
        if not editmode:
            defaultwidgets = form.widgetswithdefaults()
            defaultvalues = defaults.default_values(defaultwidgets, feature,
                                                    layer)
            defaultvalues.update(featureform.loadsavedvalues(layer))

        values.update(defaultvalues)

        initconfig = dict(form=form, canvas=self.canvas, editmode=editmode)

        config = dict(editmode=editmode,
                      layers=QgsMapLayerRegistry.instance().mapLayers(),
                      feature=feature)

        self.add_widget(FeatureFormWidgetEditor,
                        values,
                        callback,
                        config,
                        initconfig=initconfig)
Example #2
0
    def load_feature_form(self, feature, form, editmode, clear=True, callback=None,
                          cancel_callback=None):
        """
        Opens the form for the given feature.
        """
        def _sink(_):
            pass

        if not callback:
            callback = _sink

        if not cancel_callback:
            cancel_callback = _sink

        if clear:
            # Clear all the other open widgets that might be open.
            self.clear(dontemit=True)

        layer = form.QGISLayer
        attributes = feature.attributes()

        fields = [field.name().lower() for field in layer.pendingFields()]
        # Something is strange with default values and spatilite. Just delete them for now.
        if layer.dataProvider().name() == 'spatialite':
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            for index in pkindexes:
                del fields[index]
                del attributes[index]
        values = CaseInsensitiveDict(zip(fields, attributes))

        savedvalues = {}
        if not editmode:
            savedvalues = featureform.loadsavedvalues(form)
            values.update(savedvalues)
            if form.template_values:
                values.update(form.template_values)

        suppressform = form.suppressform
        if editmode:
            suppressform = False

        initconfig = dict(form=form,
                          canvas=self.canvas,
                          editmode=editmode,
                          defaults=savedvalues,
                          suppressform=suppressform,
                          feature=feature)

        config = dict(editmode=editmode,
                      layers=QgsMapLayerRegistry.instance().mapLayers(),
                      feature=feature)

        if self.project:
            layertools = self.project.layer_tools(layer)
            config['tools'] = layertools
        self.add_widget(FeatureFormWidgetEditor, values, callback, config,
                        initconfig=initconfig,
                        cancel_callback=cancel_callback)
Example #3
0
 def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
     super(FeatureFormBase, self).__init__(parent)
     self.form = form
     self.formconfig = formconfig
     self.boundwidgets = CaseInsensitiveDict()
     self.requiredfields = CaseInsensitiveDict()
     self.feature = feature
     self.defaults = defaults
     self.bindingvalues = {}
     self.editingmode = kwargs.get("editmode", False)
Example #4
0
    def load_feature_form(self, feature, form, editmode, clear=True, callback=None):
        """
        Opens the form for the given feature.
        """
        def _sink(_):
            pass

        if not callback:
            callback = _sink

        if clear:
            # Clear all the other open widgets that might be open.
            self.clear(dontemit=True)

        # One capture geometry, even for sub forms?
        # HACK Remove me and do something smarter
        roam.qgisfunctions.capturegeometry = feature.geometry()

        # Hold a reference to the fields because QGIS will let the
        # go out of scope and we get crashes. Yay!
        layer = form.QGISLayer
        self.fields = feature.fields()
        attributes = feature.attributes()

        fields = [field.name().lower() for field in self.fields]
        # Something is strange with default values and spatilite. Just delete them for now.
        if layer.dataProvider().name() == 'spatialite':
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            for index in pkindexes:
                del fields[index]
                del attributes[index]
        values = CaseInsensitiveDict(zip(fields, attributes))

        defaultvalues = {}
        if not editmode:
            defaultwidgets = form.widgetswithdefaults()
            defaultvalues = defaults.default_values(defaultwidgets, feature, layer)
            defaultvalues.update(featureform.loadsavedvalues(layer))

        values.update(defaultvalues)

        initconfig = dict(form=form,
                          canvas=self.canvas,
                          editmode=editmode,
                          defaults=defaultvalues)

        config = dict(editmode=editmode,
                      layers=QgsMapLayerRegistry.instance().mapLayers(),
                      feature=feature)

        if self.project:
            layertools = self.project.layer_tools(layer)
            config['tools'] = layertools
        self.add_widget(FeatureFormWidgetEditor, values, callback, config, initconfig=initconfig)
Example #5
0
    def load_feature_form(self,
                          feature,
                          form,
                          editmode,
                          clear=True,
                          callback=None):
        """
        Opens the form for the given feature.
        """
        def _sink(_):
            pass

        if not callback:
            callback = _sink

        if clear:
            # Clear all the other open widgets that might be open.
            self.clear(dontemit=True)

        layer = form.QGISLayer
        attributes = feature.attributes()

        fields = [field.name().lower() for field in layer.pendingFields()]
        # Something is strange with default values and spatilite. Just delete them for now.
        if layer.dataProvider().name() == 'spatialite':
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            for index in pkindexes:
                del fields[index]
                del attributes[index]
        values = CaseInsensitiveDict(zip(fields, attributes))

        savedvalues = {}
        if not editmode:
            savedvalues = featureform.loadsavedvalues(layer)
            values.update(savedvalues)

        initconfig = dict(form=form,
                          canvas=self.canvas,
                          editmode=editmode,
                          defaults=savedvalues)

        config = dict(editmode=editmode,
                      layers=QgsMapLayerRegistry.instance().mapLayers(),
                      feature=feature)

        if self.project:
            layertools = self.project.layer_tools(layer)
            config['tools'] = layertools
        self.add_widget(FeatureFormWidgetEditor,
                        values,
                        callback,
                        config,
                        initconfig=initconfig)
Example #6
0
 def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
     super(FeatureFormBase, self).__init__(parent)
     self.form = form
     self.formconfig = formconfig
     self.boundwidgets = CaseInsensitiveDict()
     self.requiredfields = CaseInsensitiveDict()
     self.feature = feature
     self.geomwidget = None
     self.defaults = defaults
     self.bindingvalues = CaseInsensitiveDict()
     self.editingmode = kwargs.get("editmode", False)
     self.widgetidlookup = {}
     self._has_save_buttons = False
     self.star_all_button = QAction(QIcon(":/icons/save_default_all"), "Star All", self, triggered=self.star_all)
Example #7
0
    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.items():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.items():
            value = nullcheck(value)
            try:
                wrapper = self.boundwidgets[field]
                if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                    if value and not os.path.exists(value):
                        value = os.path.join(self.form.project.image_folder,
                                             value)

                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug(
                    "Can't find control for field {}. Ignoring".format(field))

        self.validateall()
        if self.geomwidget and self.feature:
            self.geomwidget.set_geometry(self.feature.geometry())
Example #8
0
    def getvalues(self, no_defaults=False):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.items():
            if not no_defaults and wrapper.get_default_value_on_save:
                value = self.widget_default(field)
            else:
                value = wrapper.value()
                extradata = wrapper.extraData()
                values.update(extradata)

            # TODO this should put pulled out and unit tested. MOVE ME!
            # NOTE: This is photo widget stuff and really really doesn't belong here.
            if hasattr(
                    wrapper,
                    'savetofile') and wrapper.savetofile and wrapper.saveable:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues
Example #9
0
 def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
     super(FeatureFormBase, self).__init__(parent)
     self.form = form
     self.formconfig = formconfig
     self.boundwidgets = CaseInsensitiveDict()
     self.requiredfields = CaseInsensitiveDict()
     self.feature = feature
     self.defaults = defaults
     self.bindingvalues = {}
Example #10
0
    def openform(self, form, feature, project, editmode):
        """
        Opens a form for the given feature.
        """

        roam.qgisfunctions.capturegeometry = feature.geometry()

        defaults = {}
        layer = form.QGISLayer
        if not editmode:
            defaults = getdefaults(form.widgetswithdefaults(), feature, layer, self.canvas)

        self.actionDelete.setVisible(editmode)

        for field, value in defaults.iteritems():
            feature[field] = value

        self.formvalidation(passed=True)
        self.feature = feature
        # Hold a reference to the fields because QGIS will let the
        # go out of scope and we get crashes. Yay!
        self.fields = self.feature.fields()
        self.featureform = form.create_featureform(feature, defaults, canvas=self.canvas)
        self.featureform.editingmode = editmode
        self.featureform.rejected.connect(self.formrejected)
        self.featureform.enablesave.connect(self.actionSave.setEnabled)

        # Call the pre loading events for the form
        layers = QgsMapLayerRegistry.instance().mapLayers()

        self.project = project
        fields = [field.name().lower() for field in self.fields]
        attributes = feature.attributes()

        if layer.dataProvider().name() == 'spatialite':
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            for index in pkindexes:
                del fields[index]
                del attributes[index]

        values = CaseInsensitiveDict(zip(fields, attributes))

        try:
            self.featureform.load(feature, layers, values)
        except featureform.RejectedException as rejected:
            self.formrejected(rejected.message, rejected.level)
            return

        self.featureform.formvalidation.connect(self.formvalidation)
        self.featureform.helprequest.connect(self.helprequest.emit)
        self.featureform.bindvalues(values)
        self.featureform.showlargewidget.connect(self.setlargewidget)

        self.actionSave.setVisible(True)
        self.setwidget(self.featureform)

        self.featureform.loaded()
Example #11
0
    def getvalues(self, no_defaults=False):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            if not no_defaults and wrapper.get_default_value_on_save:
                value = self.widget_default(field)
            else:
                value = wrapper.value()
                extradata = wrapper.extraData()
                values.update(extradata)

            # TODO this should put pulled out and unit tested. MOVE ME!
            # NOTE: This is photo widget stuff and really really doesn't belong here.
            if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues
Example #12
0
def values_from_feature(feature, layer):
    attributes = feature.attributes()
    fields = [field.name().lower() for field in feature.fields()]
    if layer.dataProvider().name() == 'spatialite':
        pkindexes = layer.dataProvider().pkAttributeIndexes()
        for index in pkindexes:
            del fields[index]
            del attributes[index]
    values = CaseInsensitiveDict(zip(fields, attributes))
    return values
Example #13
0
 def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
     super(FeatureFormBase, self).__init__(parent)
     self.form = form
     self.formconfig = formconfig
     self.boundwidgets = CaseInsensitiveDict()
     self.requiredfields = CaseInsensitiveDict()
     self.feature = feature
     self.geomwidget = None
     self.defaults = defaults
     self.bindingvalues = CaseInsensitiveDict()
     self.editingmode = kwargs.get("editmode", False)
     self.widgetidlookup = {}
Example #14
0
def values_from_feature(feature, safe_names=False):
    def escape(value):
        if safe_names:
            value = value.replace(" ", "_")
            return value
        else:
            return value

    attributes = feature.attributes()
    fields = [escape(field.name().lower()) for field in feature.fields()]
    values = CaseInsensitiveDict(zip(fields, attributes))
    return values
Example #15
0
    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues
Example #16
0
    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.iteritems():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug("Can't find control for field {}. Ignoring".format(field))

        self.validateall()
Example #17
0
    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            # TODO this should put pulled out and unit tested
            if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''
                print value

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str, int)
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object, dict)

    def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.defaults = defaults
        self.bindingvalues = CaseInsensitiveDict()
        self.editingmode = kwargs.get("editmode", False)

    def open_large_widget(self, widgettype, lastvalue, callback, config=None):
        self.showlargewidget.emit(widgettype, lastvalue, callback, config)

    @property
    def is_capturing(self):
        return self.editingmode == False

    @is_capturing.setter
    def is_capturing(self, value):
        self.editingmode = not value

    def updaterequired(self, value):
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self):
        widgetwrappers = self.boundwidgets.itervalues()
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        widgetsconfig = self.formconfig['widgets']

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {field.name().lower():field for field in layer.pendingFields().toList()}

        for config in widgetsconfig:
            widgettype = config['widget']
            field = config['field']
            if not field:
                utils.info("Skipping widget. No field defined")
                continue

            field = field.lower()

            if field in self.boundwidgets:
                utils.warning("Sorry you can't bind the same field ({}) twice.".format(field))
                utils.warning("{} for field {} has been ignored in setup".format(widget, field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                widget = roam.editorwidgets.core.createwidget(widgettype)
                config['hidden'] = True
                utils.info("No widget named {} found so we have made one.".format(field))

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.debug("No label found for {}".format(field))

            widgetconfig = config.get('config', {})
            qgsfield = fields[field]
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(widgettype=widgettype,
                                                                      layer=self.form.QGISLayer,
                                                                      field=qgsfield,
                                                                      widget=widget,
                                                                      label=label,
                                                                      config=widgetconfig)
            except EditorWidgetException as ex:
                utils.exception(ex)
                continue

            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            widgetwrapper.required = config.get('required', False)

            widgetwrapper.valuechanged.connect(self.updaterequired)
            widgetwrapper.largewidgetrequest.connect(RoamEvents.show_widget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper

    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.iteritems():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                wrapper = self.boundwidgets[field]
                if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                    if value and not os.path.exists(value):
                        value = os.path.join(self.form.project.image_folder, value)

                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug("Can't find control for field {}. Ignoring".format(field))

        self.validateall()

    def bind_feature(self, feature):
        self.feature = feature
        values = self.form.values_from_feature(feature)
        self.bindvalues(values)

    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            # TODO this should put pulled out and unit tested
            if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name):
        regex = QRegExp("^{}$".format(name))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)

    def widget_default(self, name):
        """
        Return the default value for the given widget
        """
        try:
            widgetconfig = self.form.widget_by_field(name)
        except IndexError:
            raise KeyError("Widget with name {} not found on form".format(name))

        if not 'default' in widgetconfig:
            raise KeyError('Default value not defined for this field {}'.format(name))

        return defaults.widget_default(widgetconfig, self.feature, self.form.QGISLayer)

    def close_form(self, reason=None, level=1):
        self.rejected.emit(reason, level)
Example #19
0
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str, int)
    accepted = pyqtSignal()
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object, dict)

    def __init__(self, form, formconfig, feature, defaults, parent, *args,
                 **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.geomwidget = None
        self.defaults = defaults
        self.bindingvalues = CaseInsensitiveDict()
        self.editingmode = kwargs.get("editmode", False)
        self.widgetidlookup = {}
        self._has_save_buttons = False
        self.star_all_button = QAction(QIcon(":/icons/save_default_all"),
                                       "Star All",
                                       self,
                                       triggered=self.star_all)

    def open_large_widget(self, widgettype, lastvalue, callback, config=None):
        self.showlargewidget.emit(widgettype, lastvalue, callback, config)

    def form_actions(self):
        builtin = []
        if self._has_save_buttons:
            builtin.append(self.star_all_button)
        useractions = self.user_form_actions()
        if not useractions:
            useractions = []
        return builtin + useractions

    def star_all(self):
        """
        Star or unstar all buttons on the form.
        :param star_all: True to star all buttons on the form.
        """
        checked = self.star_all_button.text() == "Star All"
        buttons = self._field_save_buttons()
        for button in buttons:
            button.setChecked(checked)

        if checked:
            self.star_all_button.setText("Star None")
        else:
            self.star_all_button.setText("Star All")

    @property
    def is_capturing(self):
        return self.editingmode == False

    @is_capturing.setter
    def is_capturing(self, value):
        self.editingmode = not value

    def updaterequired(self, value):
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self):
        widgetwrappers = self.boundwidgets.values()
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        self.geomwidget = self.findcontrol("__geomwidget")

        widgetsconfig = copy.deepcopy(self.formconfig['widgets'])

        try:
            widgetsconfig = self.get_widgets(widgetsconfig)
        except AttributeError:
            pass

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {
            field.name().lower(): field
            for field in layer.fields().toList()
        }

        # Build a lookup for events
        self.events = collections.defaultdict(list)
        for event in self.form.events:
            self.events[event['source']].append(event)

        widgetsconfig = copy.deepcopy(widgetsconfig)
        self.sectionwidgets = {}

        currentsection = None
        for config in widgetsconfig:
            widgettype = config['widget']
            if widgettype == "Section":
                name = config['name']
                currentsection = name
                self.sectionwidgets[name] = []
                continue

            field = config['field']
            if not field:
                utils.info("Skipping widget. No field defined")
                continue

            field = field.lower()

            if field in self.boundwidgets:
                utils.warning(
                    "Can't bind the same field ({}) twice.".format(field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                widget = roam.editorwidgets.core.createwidget(widgettype)
                config['hidden'] = True
                utils.info(
                    "No widget named {} found so we have made one.".format(
                        field))

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.debug("No label found for {}".format(field))

            widgetconfig = config.get('config', {})
            widgetconfig['formwidget'] = self
            try:
                qgsfield = fields[field]
            except KeyError:
                utils.log("No field for ({}) found".format(field))
                continue

            context = dict(project=self.form.project,
                           form=self.form,
                           featureform=self)
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(
                    widgettype=widgettype,
                    layer=self.form.QGISLayer,
                    field=qgsfield,
                    widget=widget,
                    label=label,
                    config=widgetconfig,
                    context=context,
                    main_config=config)
            except EditorWidgetException as ex:
                utils.exception(ex)
                continue

            widgetwrapper.default_events = config.get('default_events',
                                                      ['capture'])
            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            widgetwrapper.newstyleform = self.formconfig.get("newstyle", False)
            widgetwrapper.required = config.get('required', False)

            # Only connect widgets that have events
            if widgetwrapper.id in self.events:
                widgetwrapper.valuechanged.connect(
                    partial(self.check_for_update_events, widgetwrapper))

            widgetwrapper.valuechanged.connect(self.updaterequired)

            try:
                changedslot = getattr(self, "widget_{}_changed".format(field))
                widgetwrapper.valuechanged.connect(changedslot)
            except AttributeError:
                pass

            widgetwrapper.largewidgetrequest.connect(
                RoamEvents.show_widget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper
            try:
                self.widgetidlookup[config['_id']] = widgetwrapper
            except KeyError:
                pass

            if currentsection:
                self.sectionwidgets[currentsection].append(widgetwrapper)

    def widgets_for_section(self, name):
        try:
            return self.sectionwidgets[name]
        except KeyError:
            return []

    def get_widget_from_id(self, id):
        try:
            return self.widgetidlookup[id]
        except KeyError:
            return None

    def check_for_update_events(self, widget, value):
        if not self.feature:
            return

        from qgis.core import QgsExpression, QgsExpressionContext, QgsExpressionContextScope
        # If we don't have any events for this widgets just get out now
        if not widget.id in self.events:
            return

        events = self.events[widget.id]
        events = [
            event for event in events if event['event'].lower() == 'update'
        ]
        if not events:
            return

        feature = self.to_feature(no_defaults=True)

        for event in events:
            action = event['action'].lower()
            targetid = event['target']
            if targetid == widget.id:
                utils.log(
                    "Can't connect events to the same widget. ID {}".format(
                        targetid))
                continue

            widget = self.get_widget_from_id(targetid)

            if not widget:
                utils.log(
                    "Can't find widget for id {} in form".format(targetid))
                continue

            condition = event['condition']
            expression = event['value']

            context = QgsExpressionContext()
            scope = QgsExpressionContextScope()
            scope.setVariable("value", value)
            scope.setVariable("field", widget.field)
            context.setFeature(feature)
            context.appendScope(scope)

            conditionexp = QgsExpression(condition)
            exp = QgsExpression(expression)

            if action.lower() == "show":
                widget.hidden = not conditionexp.evaluate(context)
            if action.lower() == "hide":
                widget.hidden = conditionexp.evaluate(context)
            if action == 'widget expression':
                if conditionexp.evaluate(context):
                    newvalue = self.widget_default(field, feature=feature)
                    widget.setvalue(newvalue)
            if action == 'set value':
                if conditionexp.evaluate(context):
                    newvalue = exp.evaluate(context)
                    widget.setvalue(newvalue)

    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.items():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.items():
            value = nullcheck(value)
            try:
                wrapper = self.boundwidgets[field]
                if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                    if value and not os.path.exists(value):
                        value = os.path.join(self.form.project.image_folder,
                                             value)

                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug(
                    "Can't find control for field {}. Ignoring".format(field))

        self.validateall()
        if self.geomwidget and self.feature:
            self.geomwidget.set_geometry(self.feature.geometry())

    def bind_feature(self, feature):
        self.feature = feature
        values = self.form.values_from_feature(feature)
        self.bindvalues(values)

    def getvalues(self, no_defaults=False):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.items():
            if not no_defaults and wrapper.get_default_value_on_save:
                value = self.widget_default(field)
            else:
                value = wrapper.value()
                extradata = wrapper.extraData()
                values.update(extradata)

            # TODO this should put pulled out and unit tested. MOVE ME!
            # NOTE: This is photo widget stuff and really really doesn't belong here.
            if hasattr(
                    wrapper,
                    'savetofile') and wrapper.savetofile and wrapper.saveable:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name, all=False):
        regex = QRegExp("^{}$".format(QRegExp.escape(name)))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            if all:
                return self.findChildren(QWidget, regex)
            else:
                widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _field_save_buttons(self):
        """
        Return all the save buttons for the form.
        :return:
        """
        for field in self.boundwidgets.keys():
            name = "{}_save".format(field)
            savebutton = self.findcontrol(name)
            if savebutton:
                yield savebutton

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)
        self._has_save_buttons = True

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)

    def widget_default(self, name, feature=None):
        """
        Return the default value for the given widget
        """
        try:
            widgetconfig = self.form.widget_by_field(name)
        except IndexError:
            raise KeyError(
                "Widget with name {} not found on form".format(name))

        if not 'default' in widgetconfig:
            raise KeyError(
                'Default value not defined for this field {}'.format(name))

        if not feature:
            feature = self.feature

        return defaults.widget_default(widgetconfig, feature,
                                       self.form.QGISLayer)

    def close_form(self, reason=None, level=1):
        self.rejected.emit(reason, level)

    def to_feature(self, no_defaults=False):
        """
        Create a QgsFeature from the current form values
        """
        if not self.feature:
            return

        feature = QgsFeature(self.feature.fields())
        feature.setGeometry(QgsGeometry(self.feature.geometry()))
        try:
            values, _ = self.getvalues(no_defaults=no_defaults)
        except TypeError:
            values, _ = self.getvalues()

        self.updatefeautrefields(feature, values)
        self.update_geometry(feature)
        return feature
Example #20
0
File: utils.py Project: lmotta/Roam
def values_from_feature(feature):
    attributes = feature.attributes()
    fields = [field.name().lower() for field in feature.fields()]
    values = CaseInsensitiveDict(zip(fields, attributes))
    return values
Example #21
0
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str)
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object, dict)

    def __init__(self, form, formconfig, feature, defaults, parent, *args,
                 **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.defaults = defaults
        self.bindingvalues = {}
        self.editingmode = False

    def _installeventfilters(self, widgettype):
        for widget in self.findChildren(widgettype):
            widget.installEventFilter(self)

    def eventFilter(self, object, event):
        # Hack I really don't like this but there doesn't seem to be a better way at the
        # moment
        if event.type() == QEvent.FocusIn:
            RoamEvents.openkeyboard.emit()
        return False

    def updaterequired(self, field, passed):
        self.requiredfields[field.name()] = passed
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self, widgetwrappers):
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        widgetsconfig = self.formconfig['widgets']

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {
            field.name().lower(): field
            for field in layer.pendingFields().toList()
        }

        for config in widgetsconfig:
            widgettype = config['widget']
            field = config['field']
            if not field:
                utils.warning("Skipping widget. No field defined")
                continue

            field = field.lower()

            if field in self.boundwidgets:
                utils.warning(
                    "Sorry you can't bind the same field ({}) twice.".format(
                        field))
                utils.warning(
                    "{} for field {} has been ignored in setup".format(
                        widget, field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                utils.warning("No widget named {} found".format(field))
                continue

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.warning("Not label found for {}".format(field))

            widgetconfig = config.get('config', {})
            qgsfield = fields[field]
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(
                    widgettype=widgettype,
                    layer=self.form.QGISLayer,
                    field=qgsfield,
                    widget=widget,
                    label=label,
                    config=widgetconfig)
            except EditorWidgetException as ex:
                utils.warning(ex.message)
                continue

            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            if config.get('required', False) and not widgetwrapper.hidden:
                # All widgets state off as false unless told otherwise
                self.requiredfields[field] = False
                widgetwrapper.setrequired()
                widgetwrapper.validationupdate.connect(self.updaterequired)

            widgetwrapper.largewidgetrequest.connect(self.showlargewidget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper

    def bindvalues(self, values):
        """
        Bind the values to the form.
        """
        self.bindingvalues = values
        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.info(
                    "Can't find control for field {}. Ignoring".format(field))

        self.validateall(self.boundwidgets.itervalues())

    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            print value
            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name):
        regex = QRegExp("^{}$".format(name))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)
Example #22
0
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str, int)
    accepted = pyqtSignal()
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object, dict)

    def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.geomwidget = None
        self.defaults = defaults
        self.bindingvalues = CaseInsensitiveDict()
        self.editingmode = kwargs.get("editmode", False)
        self.widgetidlookup = {}

    def open_large_widget(self, widgettype, lastvalue, callback, config=None):
        self.showlargewidget.emit(widgettype, lastvalue, callback, config)

    @property
    def is_capturing(self):
        return self.editingmode == False

    @is_capturing.setter
    def is_capturing(self, value):
        self.editingmode = not value

    def updaterequired(self, value):
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self):
        widgetwrappers = self.boundwidgets.itervalues()
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        self.geomwidget = self.findcontrol("__geomwidget")

        widgetsconfig = copy.deepcopy(self.formconfig['widgets'])

        try:
            widgetsconfig = self.get_widgets(widgetsconfig)
        except AttributeError:
            pass

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {field.name().lower():field for field in layer.pendingFields().toList()}

        # Build a lookup for events
        self.events = collections.defaultdict(list)
        for event in self.form.events:
            self.events[event['source']].append(event)

        widgetsconfig = copy.deepcopy(widgetsconfig)
        self.sectionwidgets = {}

        currentsection = None
        for config in widgetsconfig:
            widgettype = config['widget']
            if widgettype == "Section":
                name = config['name']
                currentsection = name
                self.sectionwidgets[name] = []
                continue

            field = config['field']
            if not field:
                utils.info("Skipping widget. No field defined")
                continue

            field = field.lower()

            if field in self.boundwidgets:
                utils.warning("Can't bind the same field ({}) twice.".format(field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                widget = roam.editorwidgets.core.createwidget(widgettype)
                config['hidden'] = True
                utils.info("No widget named {} found so we have made one.".format(field))

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.debug("No label found for {}".format(field))

            widgetconfig = config.get('config', {})
            widgetconfig['formwidget'] = self
            try:
                qgsfield = fields[field]
            except KeyError:
                utils.log("No field for ({}) found".format(field))

            context = dict(project=self.form.project,
                           form=self.form,
                           featureform=self)
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(widgettype=widgettype,
                                                                      layer=self.form.QGISLayer,
                                                                      field=qgsfield,
                                                                      widget=widget,
                                                                      label=label,
                                                                      config=widgetconfig,
                                                                      context=context)
            except EditorWidgetException as ex:
                utils.exception(ex)
                continue

            widgetwrapper.default_events = config.get('default_events', ['capture'])
            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            widgetwrapper.newstyleform = self.formconfig.get("newstyle", False)
            widgetwrapper.required = config.get('required', False)
            widgetwrapper.id = config.get('_id', '')

            # Only connect widgets that have events
            if widgetwrapper.id in self.events:
                widgetwrapper.valuechanged.connect(partial(self.check_for_update_events, widgetwrapper))

            widgetwrapper.valuechanged.connect(self.updaterequired)

            try:
                changedslot = getattr(self, "widget_{}_changed".format(field))
                widgetwrapper.valuechanged.connect(changedslot)
            except AttributeError:
                pass

            widgetwrapper.largewidgetrequest.connect(RoamEvents.show_widget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper
            try:
                self.widgetidlookup[config['_id']] = widgetwrapper
            except KeyError:
                pass

            if currentsection:
                self.sectionwidgets[currentsection].append(widgetwrapper)

    def widgets_for_section(self, name):
        try:
            return self.sectionwidgets[name]
        except KeyError:
            return []

    def get_widget_from_id(self, id):
        try:
            return self.widgetidlookup[id]
        except KeyError:
            return None

    def check_for_update_events(self, widget, value):
        if not self.feature:
            return

        from qgis.core import QgsExpression, QgsExpressionContext, QgsExpressionContextScope
        # If we don't have any events for this widgets just get out now
        if not widget.id in self.events:
            return

        events = self.events[widget.id]
        events = [event for event in events if event['event'].lower() == 'update']
        if not events:
            return

        feature = self.to_feature(no_defaults=True)

        for event in events:
            action = event['action'].lower()
            targetid = event['target']
            if targetid == widget.id:
                utils.log("Can't connect events to the same widget. ID {}".format(targetid))
                continue

            widget = self.get_widget_from_id(targetid)

            if not widget:
                utils.log("Can't find widget for id {} in form".format(targetid))
                continue

            condition = event['condition']
            expression = event['value']

            context = QgsExpressionContext()
            scope = QgsExpressionContextScope()
            scope.setVariable("value", value)
            scope.setVariable("field", widget.field)
            context.setFeature(feature)
            context.appendScope(scope)

            conditionexp = QgsExpression(condition)
            exp = QgsExpression(expression)

            if action.lower() == "show":
                widget.hidden = not conditionexp.evaluate(context)
            if action.lower() == "hide":
                widget.hidden = conditionexp.evaluate(context)
            if action == 'widget expression':
                if conditionexp.evaluate(context):
                    newvalue = self.widget_default(field, feature=feature)
                    widget.setvalue(newvalue)
            if action == 'set value':
                if conditionexp.evaluate(context):
                    newvalue = exp.evaluate(context)
                    widget.setvalue(newvalue)

    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.iteritems():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                wrapper = self.boundwidgets[field]
                if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                    if value and not os.path.exists(value):
                        value = os.path.join(self.form.project.image_folder, value)

                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug("Can't find control for field {}. Ignoring".format(field))

        self.validateall()
        if self.geomwidget and self.feature:
            self.geomwidget.set_geometry(self.feature.geometry())

    def bind_feature(self, feature):
        self.feature = feature
        values = self.form.values_from_feature(feature)
        self.bindvalues(values)

    def getvalues(self, no_defaults=False):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            if not no_defaults and wrapper.get_default_value_on_save:
                value = self.widget_default(field)
            else:
                value = wrapper.value()
                extradata = wrapper.extraData()
                values.update(extradata)

            # TODO this should put pulled out and unit tested. MOVE ME!
            # NOTE: This is photo widget stuff and really really doesn't belong here.
            if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name):
        regex = QRegExp("^{}$".format(QRegExp.escape(name)))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)

    def widget_default(self, name, feature=None):
        """
        Return the default value for the given widget
        """
        try:
            widgetconfig = self.form.widget_by_field(name)
        except IndexError:
            raise KeyError("Widget with name {} not found on form".format(name))

        if not 'default' in widgetconfig:
            raise KeyError('Default value not defined for this field {}'.format(name))

        if not feature:
            feature = self.feature

        return defaults.widget_default(widgetconfig, feature, self.form.QGISLayer)

    def close_form(self, reason=None, level=1):
        self.rejected.emit(reason, level)

    def to_feature(self, no_defaults=False):
        """
        Create a QgsFeature from the current form values
        """
        if not self.feature:
            return

        feature = QgsFeature(self.feature.fields())
        feature.setGeometry(QgsGeometry(self.feature.geometry()))
        try:
            values, _ = self.getvalues(no_defaults=no_defaults)
        except TypeError:
            values, _ = self.getvalues()

        self.updatefeautrefields(feature, values)
        self.update_geometry(feature)
        return feature
Example #23
0
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str, int)
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object, dict)

    def __init__(self, form, formconfig, feature, defaults, parent, *args,
                 **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.geomwidget = None
        self.defaults = defaults
        self.bindingvalues = CaseInsensitiveDict()
        self.editingmode = kwargs.get("editmode", False)

    def open_large_widget(self, widgettype, lastvalue, callback, config=None):
        self.showlargewidget.emit(widgettype, lastvalue, callback, config)

    @property
    def is_capturing(self):
        return self.editingmode == False

    @is_capturing.setter
    def is_capturing(self, value):
        self.editingmode = not value

    def updaterequired(self, value):
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self):
        widgetwrappers = self.boundwidgets.itervalues()
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        self.geomwidget = self.findcontrol("__geomwidget")

        widgetsconfig = self.formconfig['widgets']

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {
            field.name().lower(): field
            for field in layer.pendingFields().toList()
        }

        for config in widgetsconfig:
            widgettype = config['widget']
            field = config['field']
            if not field:
                utils.info("Skipping widget. No field defined")
                continue

            field = field.lower()

            if field in self.boundwidgets:
                utils.warning(
                    "Can't bind the same field ({}) twice.".format(field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                widget = roam.editorwidgets.core.createwidget(widgettype)
                config['hidden'] = True
                utils.info(
                    "No widget named {} found so we have made one.".format(
                        field))

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.debug("No label found for {}".format(field))

            widgetconfig = config.get('config', {})
            qgsfield = fields[field]
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(
                    widgettype=widgettype,
                    layer=self.form.QGISLayer,
                    field=qgsfield,
                    widget=widget,
                    label=label,
                    config=widgetconfig)
            except EditorWidgetException as ex:
                utils.exception(ex)
                continue

            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            widgetwrapper.required = config.get('required', False)

            widgetwrapper.valuechanged.connect(self.updaterequired)
            widgetwrapper.largewidgetrequest.connect(
                RoamEvents.show_widget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper

    def bindvalues(self, values, update=False):
        """
        Bind the values to the form.
        """
        if not update:
            self.bindingvalues = CaseInsensitiveDict(values)
        else:
            for key, value in values.iteritems():
                try:
                    self.bindingvalues[key] = value
                except KeyError:
                    continue

        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                wrapper = self.boundwidgets[field]
                if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                    if value and not os.path.exists(value):
                        value = os.path.join(self.form.project.image_folder,
                                             value)

                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.debug(
                    "Can't find control for field {}. Ignoring".format(field))

        self.validateall()
        if self.geomwidget and self.feature:
            self.geomwidget.set_geometry(self.feature.geometry())

    def bind_feature(self, feature):
        self.feature = feature
        values = self.form.values_from_feature(feature)
        self.bindvalues(values)

    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            # TODO this should put pulled out and unit tested
            if hasattr(wrapper, 'savetofile') and wrapper.savetofile:
                if wrapper.filename and self.editingmode:
                    name = os.path.basename(wrapper.filename)
                    name, extension = os.path.splitext(name)

                    if wrapper.modified:
                        if not name.endswith("_edited"):
                            newend = "_edited{}".format(extension)
                            value = name + newend
                        else:
                            value = os.path.basename(wrapper.filename)
                    else:
                        value = os.path.basename(wrapper.filename)
                else:
                    if wrapper.modified:
                        value = wrapper.get_filename()
                    else:
                        value = ''
                print value

            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name):
        regex = QRegExp("^{}$".format(QRegExp.escape(name)))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)

    def widget_default(self, name):
        """
        Return the default value for the given widget
        """
        try:
            widgetconfig = self.form.widget_by_field(name)
        except IndexError:
            raise KeyError(
                "Widget with name {} not found on form".format(name))

        if not 'default' in widgetconfig:
            raise KeyError(
                'Default value not defined for this field {}'.format(name))

        return defaults.widget_default(widgetconfig, self.feature,
                                       self.form.QGISLayer)

    def close_form(self, reason=None, level=1):
        self.rejected.emit(reason, level)
Example #24
0
class FeatureFormBase(QWidget):
    requiredfieldsupdated = pyqtSignal(bool)
    formvalidation = pyqtSignal(bool)
    helprequest = pyqtSignal(str)
    showwidget = pyqtSignal(QWidget)
    loadform = pyqtSignal()
    rejected = pyqtSignal(str)
    enablesave = pyqtSignal(bool)
    showlargewidget = pyqtSignal(object, object, object)

    def __init__(self, form, formconfig, feature, defaults, parent, *args, **kwargs):
        super(FeatureFormBase, self).__init__(parent)
        self.form = form
        self.formconfig = formconfig
        self.boundwidgets = CaseInsensitiveDict()
        self.requiredfields = CaseInsensitiveDict()
        self.feature = feature
        self.defaults = defaults
        self.bindingvalues = {}
        self.editingmode = False

    def _installeventfilters(self, widgettype):
        for widget in self.findChildren(widgettype):
            widget.installEventFilter(self)

    def eventFilter(self, object, event):
        # Hack I really don't like this but there doesn't seem to be a better way at the
        # moment
        if event.type() == QEvent.FocusIn:
            RoamEvents.openkeyboard.emit()
        return False

    def updaterequired(self, field, passed):
        self.requiredfields[field.name()] = passed
        passed = self.allpassing
        self.formvalidation.emit(passed)

    def validateall(self, widgetwrappers):
        for wrapper in widgetwrappers:
            wrapper.validate()

    def setupui(self):
        """
        Setup the widget in the form
        """
        widgetsconfig = self.formconfig['widgets']

        layer = self.form.QGISLayer
        # Crash in QGIS if you lookup a field that isn't found.
        # We just make a dict with all fields lower because QgsFields is case sensitive.
        fields = {field.name().lower():field for field in layer.pendingFields().toList()}

        for config in widgetsconfig:
            widgettype = config['widget']
            field = config['field'].lower()
            if field in self.boundwidgets:
                utils.warning("Sorry you can't bind the same field ({}) twice.".format(field))
                utils.warning("{} for field {} has been ignored in setup".format(widget, field))
                continue

            widget = self.findcontrol(field)
            if widget is None:
                utils.warning("No widget named {} found".format(field))
                continue

            label = self.findcontrol("{}_label".format(field))
            if label is None:
                utils.warning("Not label found for {}".format(field))

            widgetconfig = config.get('config', {})
            qgsfield = fields[field]
            try:
                widgetwrapper = roam.editorwidgets.core.widgetwrapper(widgettype=widgettype,
                                                                      layer=self.form.QGISLayer,
                                                                      field=qgsfield,
                                                                      widget=widget,
                                                                      label=label,
                                                                      config=widgetconfig)
            except EditorWidgetException as ex:
                utils.warning(ex.message)
                continue

            readonlyrules = config.get('read-only-rules', [])

            if self.editingmode and 'editing' in readonlyrules:
                widgetwrapper.readonly = True
            elif 'insert' in readonlyrules or 'always' in readonlyrules:
                widgetwrapper.readonly = True

            widgetwrapper.hidden = config.get('hidden', False)

            if config.get('required', False) and not widgetwrapper.hidden:
                # All widgets state off as false unless told otherwise
                self.requiredfields[field] = False
                widgetwrapper.setrequired()
                widgetwrapper.validationupdate.connect(self.updaterequired)

            widgetwrapper.largewidgetrequest.connect(self.showlargewidget.emit)

            self._bindsavebutton(field)
            self.boundwidgets[field] = widgetwrapper

    def bindvalues(self, values):
        """
        Bind the values to the form.
        """
        self.bindingvalues = values
        for field, value in values.iteritems():
            value = nullcheck(value)
            try:
                self.boundwidgets[field].setvalue(value)
            except KeyError:
                utils.info("Can't find control for field {}. Ignoring".format(field))

        self.validateall(self.boundwidgets.itervalues())

    def getvalues(self):
        def shouldsave(field):
            name = "{}_save".format(field)
            button = self.findcontrol(name)
            if not button is None:
                return button.isChecked()

        savedvalues = {}
        values = CaseInsensitiveDict(self.bindingvalues)
        for field, wrapper in self.boundwidgets.iteritems():
            value = wrapper.value()
            if shouldsave(field):
                savedvalues[field] = value
            values[field] = value

        return values, savedvalues

    def findcontrol(self, name):
        regex = QRegExp("^{}$".format(name))
        regex.setCaseSensitivity(Qt.CaseInsensitive)
        try:
            widget = self.findChildren(QWidget, regex)[0]
        except IndexError:
            widget = None
        return widget

    def _bindsavebutton(self, field):
        name = "{}_save".format(field)
        button = self.findcontrol(name)
        if button is None:
            return

        button.setCheckable(not self.editingmode)
        button.setIcon(QIcon(":/icons/save_default"))
        button.setIconSize(QSize(24, 24))
        button.setChecked(field in self.defaults)
        button.setVisible(not self.editingmode)

    def createhelplinks(self):
        def createhelplink(label, folder):
            def getHelpFile():
                # TODO We could just use the tooltip from the control to show help
                # rather then having to save out a html file.
                name = label.objectName()
                if name.endswith("_label"):
                    name = name[:-6]
                filename = "{}.html".format(name)
                filepath = os.path.join(folder, "help", filename)
                if os.path.exists(filepath):
                    return filepath
                else:
                    return None

            if label is None:
                return

            helpfile = getHelpFile()
            if helpfile:
                text = '<a href="{}">{}<a>'.format(helpfile, label.text())
                label.setText(text)
                label.linkActivated.connect(self.helprequest.emit)

        for label in self.findChildren(QLabel):
            createhelplink(label, self.form.folder)