def _init_entity_editor(self): """ Instantiates entity editor and add its widgets to the GPX import tool as tabs :return: None :rtype:None """ self.entity_editor = EntityEditorDialog(self.entity, self.model, self) self.entity_editor.buttonBox.hide() # Hide entity editor buttons self.setWindowTitle(self.entity_editor.title) for tab_text, tab_object in self.entity_editor.entity_editor_widgets.items(): if tab_text != 'no_tab': self.gpx_import_tab.addTab(tab_object, str(tab_text)) else: self.gpx_import_tab.addTab(tab_object, 'Primary')
def on_bt_save_clicked(self): """ Run when Qtablewidget button save is clicked """ geom_list = [] for key, vertex in self.vertex_dict.iteritems(): if vertex[3] == Qt.Checked: geom_list.append(QgsPoint(vertex[1], vertex[2])) if self.active_layer_geometry_typ == 0: geom = QgsGeometry.fromPoint(geom_list[0]) elif self.active_layer_geometry_typ == 1: geom = QgsGeometry.fromPolyline(geom_list) elif self.active_layer_geometry_typ == 2: geom = QgsGeometry.fromPolygon([geom_list]) geom_wkb = geom.exportToWkt() non_sp_colms = non_spatial_table_columns(self.sp_table) for key, vertex in self.vertex_dict.iteritems(): self.map_canvas.scene().removeItem(vertex[0]) self.close() self.curr_layer = vector_layer(self.sp_table, geom_column=self.sp_col) self.iface.mapCanvas().setExtent(self.curr_layer.extent()) self.iface.mapCanvas().refresh() srid = self.entity.columns[self.sp_col].srid #init form editor = EntityEditorDialog(self.entity, None, self.iface.mainWindow()) model = editor.model() # add geometry into the model setattr(model, self.sp_col, 'SRID={};{}'.format(srid, geom_wkb)) editor.exec_()
def edit_selected_steam(self, entity): id, item = self.steam_data('edit') feature_edit = True if id is None: return if isinstance(id, str): data_error = QApplication.translate('DetailsTreeView', id) QMessageBox.warning( self.iface.mainWindow(), "Edit Error", data_error ) return if item.text() == 'Social Tenure Relationship': model = self.STR_models[id] feature_edit = False ##TODO add STR wizard edit mode here. elif item.text() == format_name(self.party.short_name): feature_edit = False model = self.party_models[id] editor = EntityEditorDialog( self.party, model, self.iface.mainWindow() ) editor.exec_() else: model = self.feature_models[id] editor = EntityEditorDialog( entity, model, self.iface.mainWindow() ) editor.exec_() #root = self.find_root(entity, id) self.view.expand(item.index()) if feature_edit: self.update_edited_steam(entity, id) else: self.update_edited_steam(self.social_tenure, id)
class GPSToolDialog(WIDGET, BASE): def __init__(self, iface, entity, sp_table, sp_col, model=None, reload=True, row_number=None, entity_browser=None, allow_str_creation=True): QDialog.__init__(self, iface.mainWindow()) self.setupUi(self) self.iface = iface self.entity = entity self._entity = self.entity self.sp_table = sp_table self.sp_col = sp_col self.model = model self.reload = reload self.entity_browser = entity_browser self.row_number = row_number self.entity_editor = None gpx_view.enable_drag_drop(self.table_widget, self._table_widget_drag_enter, self._table_widget_row_dropped) self._init_entity_editor(allow_str_creation) self.active_layer = self.iface.activeLayer() self.map_canvas = self.iface.mapCanvas() self.init_gpx_file = None self.point_row_attr = None self.prev_point_row_attr = None self.prev_selected_rows = None self.gpx_layer = None self.qgs_point_list = [] self.saved = False self.geom_type = None self.uncheck_counter = 0 self.temp_layer_name = None self.temp_mem_layer = None self.prev_temp_mem_layer = None self.prev_item_position = None self.prev_item_value = None self.data_changed = None self.geometry_added = False self.drag_drop = None self.item_changed = None self.enable_save = False # Source and slot signal connections self.file_select_bt.clicked.connect(self._set_file_path) self.file_le.textChanged.connect(self._file_source_change) self.feature_type_cb.currentIndexChanged.connect(self._show_data) self.table_widget.itemClicked.connect( self._table_widget_checkbox_clicked) self.table_widget.itemSelectionChanged.connect( self._table_widget_row_selection) self.table_widget.itemDoubleClicked.connect( self._table_widget_item_double_clicked) self.table_widget.itemChanged.connect(self._table_widget_item_changed) self.select_all_bt.clicked.connect(self._select_all_items) self.clear_all_bt.clicked.connect(self._clear_all_items) self.load_bt.clicked.connect(self._save_feature) self.cancel_bt.clicked.connect(self.close) self.gpx_import_tab.currentChanged.connect(self.enable_save_button) def enable_save_button(self): self.enable_save = True self.load_bt.setEnabled(self.enable_save) def _init_entity_editor(self, allow_str_creation: bool): """ Instantiates entity editor and add its widgets to the GPX import tool as tabs :return: None :rtype:None """ self.entity_editor = EntityEditorDialog( self.entity, self.model, self, allow_str_creation=allow_str_creation) self.entity_editor.buttonBox.hide() # Hide entity editor buttons self.setWindowTitle(self.entity_editor.title) for tab_text, tab_object in self.entity_editor.entity_editor_widgets.items( ): if tab_text != 'no_tab': self.gpx_import_tab.addTab(tab_object, str(tab_text)) else: self.gpx_import_tab.addTab(tab_object, 'Primary') def _set_file_path(self): """ Open file select dialog when browse button is clicked :return: None :rtype: None """ (gpx_file, encoding) = openDialog(self) if gpx_file: self.file_le.clear() self.file_le.setText(gpx_file) def _file_source_change(self): """ Enable or disable widgets based on the validity of the file path :return: None :rtype: None """ gpx_file = str(self.file_le.text()).strip() if gpx_source.validate_file_path(gpx_file): if self.point_row_attr: self._refresh_map_canvas() self._reset_widget() self._enable_widget(gpx_file) else: self._disable_widget() def _enable_widget(self, gpx_file=None): """ Enables all widgets :return: None :rtype: None """ if self.table_widget.columnCount() > 0: self._reset_widget(gpx_file) if gpx_file == self.init_gpx_file: self._enable_disable_button_widgets(True) self.table_widget.setEnabled(True) self.feature_type_cb.setEnabled(True) def _reset_widget(self, gpx_file=None, feature_type_change=False): """ Resets widget table properties :param gpx_file: Input GPX file :param feature_type_change: Flag for feature type when changed in the combobox :return: None :rtype: None """ if gpx_file: if gpx_file != self.init_gpx_file: self._reset_table_layer() self._disable_widget() elif feature_type_change and gpx_file == self.init_gpx_file: self._reset_table_layer() self._disable_widget(True) else: self._reset_table_layer() self._disable_widget(False, True) def _remove_prev_layer(self): """ Removes previous temporary layer :return: None :rtype: None """ if self.prev_temp_mem_layer and len( gpx_view.get_layer_by_name(self.temp_layer_name)) != 0: gpx_view.remove_vertex(self.map_canvas, self.prev_point_row_attr) gpx_view.remove_map_layer(self.map_canvas, self.prev_temp_mem_layer) return True def _disable_widget(self, enable_cb=False, on_load=None): """ Disables all widgets :return: None :rtype: None """ self.feature_type_cb.setEnabled(enable_cb) if on_load: self.table_widget.setEnabled(True) self.feature_type_cb.setCurrentIndex(-1) elif self.table_widget.columnCount() > 0: self.table_widget.setEnabled(False) elif self.table_widget.columnCount() == 0: self.feature_type_cb.setCurrentIndex(-1) self._enable_disable_button_widgets() def _reset_table_layer(self): """ Calls column and row reset and removes temporary layer :return: None :return: None """ self._reset_column_rows() self._remove_prev_layer() self.prev_temp_mem_layer = None def _reset_column_rows(self): """ Removes all columns and rows in the table widget :return: None :rtype: None """ self.table_widget.setRowCount(0) self.table_widget.setColumnCount(0) def _enable_disable_button_widgets(self, state=False, load_state=None): """ Disables or enables table widget buttons :param state: True or false to enable or disable all buttons :return: None :rtype: None """ if state is not None: self.select_all_bt.setEnabled(state) self.clear_all_bt.setEnabled(state) if self.enable_save: self.load_bt.setEnabled(state) else: if self.enable_save: self.load_bt.setEnabled(load_state) def _refresh_map_canvas(self): """ Refresh map canvas on file source or feature type change :return: None :rtype: None """ curr_layer = vector_layer(self.sp_table, geom_column=self.sp_col) layer_extent = curr_layer.extent() gpx_view.remove_vertex(self.map_canvas, self.point_row_attr) self.map_canvas.setExtent(layer_extent) self.map_canvas.refresh() def _show_data(self, index): """ Initializes loading of GPX data :param index: Comboxbox item index :return: None :rtype: None """ # no need to continue if layer is None if self.active_layer is None: return gpx_file = str(self.file_le.text()).strip() feature_type = self.feature_type_cb.currentIndex() columns = 4 headers = ["", "Point Name", "Longitude", "Latitude"] if feature_type >= 0: gpx_data_source = gpx_source.open_gpx_file(gpx_file) self.gpx_layer, feature_count = gpx_source.get_feature_layer( gpx_data_source, feature_type) self.uncheck_counter = feature_count if self.gpx_layer: self.geom_type, int_geom_type = gpx_source.get_active_layer_type( self.active_layer) if self.geom_type: self._populate_data(feature_count, columns, headers) self._enable_disable_load_on_feature_type_click( int_geom_type) self.prev_temp_mem_layer = self.temp_mem_layer self.prev_point_row_attr = list(self.point_row_attr) self.init_gpx_file = gpx_file else: QMessageBox.critical( self, self.tr('Incompatibility Error'), self.tr( 'Current active layer not compatible with GPS ' 'data.\nGeometry type "POINT", "LINE" or "POLYGON"' ' is required.')) else: QMessageBox.warning( self, self.tr('Feature Type Error'), 'The selected .gpx file has no {} feature type.'.format( feature_type)) if self.table_widget.columnCount() > 0: if self.point_row_attr: self._refresh_map_canvas() self._reset_widget(gpx_file, True) return None def _populate_data(self, feature_count, columns, headers): """ Populates table widget with data and creates temporary layer :param feature_count: Number of features :param columns: Number of table widget columns :param headers: Table widget field names :return: None :rtype: None """ point_list = [] self.qgs_point_list = [] self.point_row_attr = [] self.data_changed = False table_widget = self.table_widget self.temp_layer_name = 'temp_layer' self._set_table_structure(table_widget, feature_count, columns, headers) for feature_attr, row in gpx_view.get_feature_attributes( self.gpx_layer): point = QgsPointXY(feature_attr[1], feature_attr[2]) point_list.append(point) check_box = self._set_table_widget_item(feature_attr, table_widget, row) checkbox_state = check_box.checkState() marker = gpx_view.set_feature_vertex_marker( self.map_canvas, feature_attr[1], feature_attr[2]) self.point_row_attr.append({ 'row': row, 'marker': marker, 'checkbox': check_box, 'check_state': checkbox_state, 'qgs_point': point }) self._set_table_header_property(table_widget) self._remove_prev_layer() self.temp_mem_layer = gpx_view.create_feature(self.active_layer, self.geom_type, point_list, self.temp_layer_name) gpx_view.add_map_layer(self.temp_mem_layer, ) gpx_view.set_layer_extent(self.map_canvas, self.gpx_layer) self.qgs_point_list = list(point_list) self.data_changed = True def _set_table_structure(self, table_widget, rows, columns, headers): """ Sets table widget rows, columns and headers :param rows: Number of rows :param columns: Number of columns :param headers: Table headers :param table_widget: Table widget object :return: None :rtype: None """ table_widget.setRowCount(rows) table_widget.setColumnCount(columns) table_widget.setHorizontalHeaderLabels(headers) def _set_table_widget_item(self, feature_attr, table_widget, row): """ Sets table widget items :param feature_attr: Data from the GPX feature :param table_widget: Table widget object :param row: Feature count. To be used to set table row number :return: """ check_box = None for column_num, attr in enumerate(feature_attr): if column_num == 0: check_box = self._set_checkbox_item() table_widget.setItem(row, column_num, check_box) column_num += 1 if type(attr) is not str: attr = str(attr) table_widget.setItem(row, column_num, QTableWidgetItem(attr)) return check_box def _set_checkbox_item(self): """ Sets the checkbox item :return: Table widget item :rtype: Object """ check_box = QTableWidgetItem() check_box.setCheckState(Qt.Checked) check_box.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable) return check_box def _set_table_header_property(self, table_widget, enable=True): """ Sets table widget header properties :param table_widget: Table widget object :param enable: Parameter to set header property to True or False :return: None :rtype: None """ table_widget.resizeColumnsToContents() table_widget.horizontalHeader().setStretchLastSection(enable) def _enable_disable_load_on_feature_type_click(self, int_geom_type): """ Enables or disables load button on feature type load to the table widget based on the number of features and geometry type. :param int_geom_type: Geometry type as an integer :return: None :rtype: None """ if self.uncheck_counter > int_geom_type: self._enable_disable_button_widgets(True) elif self.uncheck_counter <= int_geom_type: self._enable_disable_button_widgets(None, False) def _table_widget_checkbox_clicked(self, item): """ Handles checkbox value change :param item: Clicked item or checkbox widget :return: None :rtype: None """ if item.checkState() == Qt.Unchecked: qgs_points = gpx_view.check_uncheck_item(self.point_row_attr, self.map_canvas, item) for qgs_point in qgs_points: self._unchecked_gpx_point(qgs_point) self._enable_disable_load_on_checkbox_click() else: qgs_points = gpx_view.check_uncheck_item(self.point_row_attr, self.map_canvas, item) for qgs_point in qgs_points: self._checked_gpx_point(qgs_point) self._enable_disable_load_on_checkbox_click() self._table_widget_row_selection() def _unchecked_gpx_point(self, qgs_point): """ Removes feature vertex from a list :param qgs_point: Feature vertex """ if qgs_point: if qgs_point in self.qgs_point_list: self.qgs_point_list.remove(qgs_point) point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) def _checked_gpx_point(self, qgs_point): """ Adds feature vertex to a list :param qgs_point: Feature vertex :return: None :rtype: None """ if qgs_point: qgs_point = gpx_view.add_to_list(self.qgs_point_list, qgs_point) if qgs_point: self.qgs_point_list.append(qgs_point) point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) def _enable_disable_load_on_checkbox_click(self): """ Counts clicks if a checkbox is checked or unchecked and enables or disables the save button. :return: None :rtype: None """ if self.data_changed: check_state_count = 0 active_geom_type = int(self.iface.activeLayer().geometryType()) for row, point_attr in enumerate(self.point_row_attr): if point_attr['check_state'] == 2 and point_attr['qgs_point']: check_state_count += 1 if check_state_count > active_geom_type: self.uncheck_counter += 1 self._enable_disable_button_widgets(None, True) elif check_state_count <= active_geom_type: self.uncheck_counter -= 1 self._enable_disable_button_widgets(None, False) def _table_widget_row_selection(self): """ Set vertex color on row selection change """ # Remove highlighting gpx_view.row_selection_change(self.map_canvas, self.point_row_attr, self.prev_selected_rows) current_selected_rows = sorted( set([index.row() for index in self.table_widget.selectedIndexes()])) # Make selection gpx_view.row_selection_change(self.map_canvas, self.point_row_attr, current_selected_rows, selection_color()) if current_selected_rows: self.prev_selected_rows = [] self.prev_selected_rows.extend(current_selected_rows) def _table_widget_drag_enter(self): """ On start of drag and drop set data change and drag and drop flag to false :return: None :rtype: None """ self.data_changed = False self.drag_drop = True def _table_widget_row_dropped(self): """ Update feature on end of drag and drop event :return: None :rtype: None """ point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) self.data_changed = True def _table_widget_item_double_clicked(self, item): """ On double click get previous item values :param item: Current item :return: None :rtype: None """ if item.column() != 0: self.prev_item_value = item.text().strip() def _table_widget_item_changed(self, item): """ On item change update GPX feature :param item: Table widget item :return: None :rtype: None """ current_item_value = item.text().strip() if self.data_changed and item.column() != 0: if current_item_value != self.prev_item_value: point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) self._enable_disable_load_on_checkbox_click() def _update_feature(self, point_list, new_point_row_attr): """ Update feature :param point_list: Updated QGS points :param new_point_row_attr: Updated GPX data attributes :return: None :rtype: None """ if point_list: self.qgs_point_list = list(point_list) gpx_view.remove_vertex(self.map_canvas, self.prev_point_row_attr) gpx_view.delete_feature(self.map_canvas, self.temp_mem_layer) self.point_row_attr = gpx_view.update_point_row_attr( self.map_canvas, self.point_row_attr, new_point_row_attr) self.point_row_attr = gpx_view.set_feature_vertices_marker( self.map_canvas, self.point_row_attr) new_geometry = gpx_view.create_geometry(self.geom_type, point_list) data_provider = self.temp_mem_layer.dataProvider() gpx_view.add_feature(data_provider, new_geometry) gpx_view.commit_feature_edits(self.temp_mem_layer) self.prev_point_row_attr = list(self.point_row_attr) else: gpx_view.remove_vertex(self.map_canvas, self.prev_point_row_attr) gpx_view.delete_feature(self.map_canvas, self.temp_mem_layer) def _select_all_items(self): """ Checks all items which are unchecked :return: None :rtype: None """ qgs_points = gpx_view.check_uncheck_item(self.point_row_attr, self.map_canvas, None, 'check') for qgs_point in qgs_points: self._checked_gpx_point(qgs_point) self._enable_disable_button_widgets(None, True) if self.data_changed or self.drag_drop: point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) self.drag_drop = None def _clear_all_items(self): """ Unchecks all items which are checked and clears the feature :return: None :rtype: None """ qgs_points = gpx_view.check_uncheck_item(self.point_row_attr, self.map_canvas, None, 'uncheck') for qgs_point in qgs_points: self._unchecked_gpx_point(qgs_point) self._enable_disable_button_widgets(None, False) point_list, new_point_row_attr = gpx_view.get_qgs_points( self.table_widget) self._update_feature(point_list, new_point_row_attr) def _save_feature(self): """ Load and save feature to current active layer """ # If qgs_point_list length is 0, then it is in edit mode so # allow attribute edit too. if len(self.qgs_point_list) > 0: new_geometry = gpx_view.create_geometry(self.geom_type, self.qgs_point_list) geometry_wkb = new_geometry.asWkt() srid = self.entity.columns[self.sp_col].srid model = self.entity_editor.model() setattr(model, self.sp_col, 'SRID={};{}'.format(srid, geometry_wkb)) self.geometry_added = True # validate empty GPS field if not self.geometry_added and self.model is None: QMessageBox.critical( self, self.tr('Missing GPS Feature Error'), self.tr( 'You have not added a GPS Feature. \n' 'Please, add a GPS feature in the Feature Import tab or\n' 'digitize a feature to add a record.')) return # prevents duplicate entry self.load_bt.setDisabled(True) self.entity_editor.save_parent_editor() if self.reload: self._reload_entity_editor() self.load_bt.setDisabled(False) else: self.close() def _reload_entity_editor(self): """ Reloads entity editor and its widget to the GPX import tab widget. :return: None :rtype: None """ self.entity_editor.clear() def closeEvent(self, event): """ Closes the import dialog when called :param event: Close event :return: None :rtype: None """ if len(gpx_view.get_layer_by_name(self.temp_layer_name)) != 0: gpx_view.remove_vertex(self.map_canvas, self.point_row_attr) gpx_view.remove_map_layer(self.map_canvas, self.temp_mem_layer) if self.point_row_attr: self._refresh_map_canvas() self.save_handler() def save_handler(self): current_model_obj = self.entity_editor.model() if self.entity_editor.saved_model is None: return # Edit mode if self.entity_browser is not None and self.row_number is not None: if self.model is not None: for i, attr in enumerate(self.entity_browser._entity_attrs): prop_idx = self.entity_browser._tableModel.index( self.row_number, i) attr_val = getattr(current_model_obj, attr) # Check if there are display formatters and apply if # one exists for the given attribute. if attr in self.entity_browser._cell_formatters: formatter = self.entity_browser._cell_formatters[attr] attr_val = formatter.format_column_value(attr_val) self.entity_browser._tableModel.setData(prop_idx, attr_val) # New record mode if self.entity_browser is not None and self.row_number is None: self.entity_browser.addModelToView(current_model_obj) self.entity_browser.recomputeRecordCount()
def load_stdm_form(self, feature_id, spatial_column): """ Loads STDM Form and collects the model added into the form so that it is saved later. :param feature_id: the ID of a feature that is last added :type feature_id: Integer :param spatial_column: The spatial column name of the layer :type spatial_column: String :return: None :rtype:NoneType """ srid = None self.current_feature = feature_id # If the digitizing save button is clicked, # the featureAdded signal is called but the # feature ids value is over 0. Return to prevent # the dialog from popping up for every feature. if feature_id > 0: return # if the feature is already in the OrderedDict don't # show the form as the model of the feature is # already populated by the form if feature_id in self.feature_models.keys(): return # If the feature is removed by the undo button, don't # load the form for it but add it # back to feature_models and don't show the form. # This happens when redo button(add feature back) is # clicked after an undo button(remove feature) if feature_id in self.removed_feature_models.keys(): self.feature_models[feature_id] = \ self.removed_feature_models[feature_id] return # If the feature is not valid, geom_wkt will be None # So don't launch form for invalid feature and delete feature geom_wkt = self.get_wkt(feature_id) if geom_wkt is None: title = QApplication.translate('STDMFieldWidget', u'Spatial Entity Form Error', None, QCoreApplication.UnicodeUTF8) msg = QApplication.translate( 'STDMFieldWidget', u'The feature you have added is invalid. \n' 'To fix this issue, check if the feature ' 'is digitized correctly. \n' 'Make sure you have added a base layer to digitize on.', None, QCoreApplication.UnicodeUTF8) # Message: Spatial column information # could not be found QMessageBox.critical(iface.mainWindow(), title, msg) return # init form self.editor = EntityEditorDialog(self.entity, None, parent=iface.mainWindow(), manage_documents=True, collect_model=True) self.model = self.editor.model() self.editor.addedModel.connect(self.on_form_saved) # get srid with EPSG text full_srid = self.layer.crs().authid().split(':') if len(full_srid) > 0: # Only extract the number srid = full_srid[1] if not geom_wkt is None: # add geometry into the model setattr(self.model, spatial_column, 'SRID={};{}'.format(srid, geom_wkt)) # open editor result = self.editor.exec_() if result < 1: self.removed_feature_models[feature_id] = None self.layer.deleteFeature(feature_id)
class STDMFieldWidget(): # Instantiate the singleton QgsEditorWidgetRegistry widgetRegistry = QgsEditorWidgetRegistry.instance() def __init__(self): self.entity = None self.widget_mapping = {} self.layer = None self.feature_models = OrderedDict() self.removed_feature_models = OrderedDict() self.current_feature = None self.editor = None def init_form(self, table, spatial_column, curr_layer): """ Initialize required methods and slots to be used in form initialization. :param table: The table name of the layer :type table: String :param spatial_column: The spatial column name of the layer :type spatial_column: String :param curr_layer: The current layer of form. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneTYpe """ try: # init form self.set_entity(table) self.set_widget_mapping() self.register_factory() self.set_widget_type(curr_layer) curr_layer.editFormConfig().setSuppress(1) try: curr_layer.featureAdded.connect( lambda feature_id: self.load_stdm_form( feature_id, spatial_column)) except Exception: pass curr_layer.featureDeleted.connect(self.on_feature_deleted) curr_layer.beforeCommitChanges.connect(self.on_digitizing_saved) except Exception as ex: LOGGER.debug(ex) def set_entity(self, source): """ Sets the layer entity of the layer based on a table name. :param source: Table name that acts as a layer source. :type source: String :return: None :rtype: NoneType """ curr_profile = current_profile() self.entity = curr_profile.entity_by_name(source) def _set_widget_type(self, layer, column, widget_type_id): """ Sets the widget type for each field into QGIS form configuration. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :param column: STDM column object :type column: Object :param widget_type_id: The widget type id which could be the default QGIS or the custom STDM widget id which is based on column.TYPE_INFO. :type widget_type_id: String :return: None :rtype:NoneType """ idx = layer.fieldNameIndex(column.name) # Set Alias/ Display names for the column names layer.addAttributeAlias(idx, column.header()) try: layer.editFormConfig().setWidgetType(idx, widget_type_id) except Exception: layer.setEditorWidgetV2(idx, widget_type_id) def set_widget_mapping(self): """ Maps each column to QGIS or STDM editor widgets. :return: None :rtype:NoneType """ self.widget_mapping.clear() for c in self.entity.columns.values(): if c.TYPE_INFO == 'SERIAL': self.widget_mapping[c] = ['Hidden', None] elif c.TYPE_INFO == 'GEOMETRY': self.widget_mapping[c] = ['TextEdit', None] else: stdm = QApplication.translate('STDMFieldWidget', u'STDM') self.widget_mapping[c] = [ u'stdm_{}'.format(c.TYPE_INFO.lower()), u'{} {}'.format(stdm, c.display_name()) ] def register_factory(self): """ Registers each widget type to a QGIS widget factory registry. :return: None :rtype: NoneType """ # The destructor has no effects. It is QGIS bug. # So restarting QGIS is required to destroy # registered stdm widgets. for widget_id_name in self.widget_mapping.values(): # add and register stdm widget type only if not widget_id_name[1] is None: widget_name = widget_id_name[1] if widget_id_name[0] not in \ self.widgetRegistry.factories().keys(): widget_factory = QGISFieldWidgetFactory(widget_name) self.widgetRegistry.registerWidget(widget_id_name[0], widget_factory) def set_widget_type(self, layer): """ Sets widget type for each fields in a layer. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :return: None :rtype: NoneType """ self.layer = layer for col, widget_id_name in \ self.widget_mapping.iteritems(): self._set_widget_type(layer, col, widget_id_name[0]) def load_stdm_form(self, feature_id, spatial_column): """ Loads STDM Form and collects the model added into the form so that it is saved later. :param feature_id: the ID of a feature that is last added :type feature_id: Integer :param spatial_column: The spatial column name of the layer :type spatial_column: String :return: None :rtype:NoneType """ srid = None self.current_feature = feature_id # If the digitizing save button is clicked, # the featureAdded signal is called but the # feature ids value is over 0. Return to prevent # the dialog from popping up for every feature. if feature_id > 0: return # if the feature is already in the OrderedDict don't # show the form as the model of the feature is # already populated by the form if feature_id in self.feature_models.keys(): return # If the feature is removed by the undo button, don't # load the form for it but add it # back to feature_models and don't show the form. # This happens when redo button(add feature back) is # clicked after an undo button(remove feature) if feature_id in self.removed_feature_models.keys(): self.feature_models[feature_id] = \ self.removed_feature_models[feature_id] return # If the feature is not valid, geom_wkt will be None # So don't launch form for invalid feature and delete feature geom_wkt = self.get_wkt(feature_id) if geom_wkt is None: title = QApplication.translate('STDMFieldWidget', u'Spatial Entity Form Error', None, QCoreApplication.UnicodeUTF8) msg = QApplication.translate( 'STDMFieldWidget', u'The feature you have added is invalid. \n' 'To fix this issue, check if the feature ' 'is digitized correctly. \n' 'Make sure you have added a base layer to digitize on.', None, QCoreApplication.UnicodeUTF8) # Message: Spatial column information # could not be found QMessageBox.critical(iface.mainWindow(), title, msg) return # init form self.editor = EntityEditorDialog(self.entity, None, parent=iface.mainWindow(), manage_documents=True, collect_model=True) self.model = self.editor.model() self.editor.addedModel.connect(self.on_form_saved) # get srid with EPSG text full_srid = self.layer.crs().authid().split(':') if len(full_srid) > 0: # Only extract the number srid = full_srid[1] if not geom_wkt is None: # add geometry into the model setattr(self.model, spatial_column, 'SRID={};{}'.format(srid, geom_wkt)) # open editor result = self.editor.exec_() if result < 1: self.removed_feature_models[feature_id] = None self.layer.deleteFeature(feature_id) def get_wkt(self, feature_id): """ Gets feature geometry in Well-Known Text format and returns it. :param feature_id: Feature id :type feature_id: Integer :return: Well-Known Text format of a geometry :rtype: WKT """ geom_wkt = None fid = feature_id request = QgsFeatureRequest() request.setFilterFid(fid) features = self.layer.getFeatures(request) # get the wkt of the geometry for feature in features: geometry = feature.geometry() if geometry.isGeosValid(): geom_wkt = feature.geometry().exportToWkt() return geom_wkt def on_form_saved(self, model): """ A slot raised when the save button is clicked in spatial unit form. It adds the feature model in feature_models ordered dictionary to be saved later. :param model: The model holding feature geometry and attributes obtained from the form :type model: SQLAlchemy Model :return: None :rtype: NoneType """ if not model is None: self.feature_models[self.current_feature] = model if self.editor.is_valid: self.editor.accept() def on_feature_deleted(self, feature_id): """ A slot raised when a feature is deleted in QGIS map canvas via the undo button. It deletes the associated model of the feature. :param feature_id: The id that is removed. :type feature_id: Integer :return: None :rtype: NoneType """ if feature_id in self.feature_models.keys(): self.removed_feature_models[feature_id] = \ self.feature_models[feature_id] del self.feature_models[feature_id] def on_digitizing_saved(self): """ A slot raised when the save button is clicked on Digitizing Toolbar of QGIS. It saves feature models created by the digitizer and STDM form to the Database. :return: None :rtype: NoneType """ ent_model = entity_model(self.entity) entity_obj = ent_model() entity_obj.saveMany(self.feature_models.values()) # Save child models if self.editor is not None: self.editor.save_children() # undo each feature created so that qgis # don't try to save the same feature again. # It will also clear all the models from # self.feature_models as on_feature_deleted # is raised when a feature is removed. for i in range(len(self.feature_models)): self.layer.undoStack().undo()
def add_entity_editor(self, party_entity, spatial_unit_entity, party_model, str_number, row_number, custom_model=None): """ Adds custom tenure info tab with editor form. It could load data if the custom_model is not none. :param party_entity: The associated party entity :type party_entity: Object :param spatial_unit_entity: The associated spatial unit entity :type spatial_unit_entity: Object :param party_model: The party model associated with custom tenure info record. :type party_model: Object :param str_number: The STR record number :type str_number: Integer :param row_number: The row number of the party entry :type row_number: Integer :param custom_model: The custom tenure model that populates the tab forms. :type custom_model: Integer :return: True if the editor is created and false if not created. :rtype: Boolean """ # Get the custom attribute entity custom_attr_entity = self.social_tenure.spu_custom_attribute_entity( spatial_unit_entity) if custom_attr_entity is None: return False if len(custom_attr_entity.columns) < 3: return False # If None then create if custom_model is None: self.entity_editors[(str_number, row_number)] = EntityEditorDialog( custom_attr_entity, parent=self.parent.custom_tenure_tab, manage_documents=False, parent_entity=self.social_tenure, exclude_columns=[ 'social_tenure_relationship_id', self.social_tenure.CUSTOM_TENURE_DUMMY_COLUMN ]) else: self.entity_editors[(str_number, row_number)] = EntityEditorDialog( custom_attr_entity, parent=self.parent.custom_tenure_tab, manage_documents=False, model=custom_model, parent_entity=self.social_tenure, exclude_columns=[ 'social_tenure_relationship_id', self.social_tenure.CUSTOM_TENURE_DUMMY_COLUMN ]) display_columns = self.textual_display_columns(party_entity) if len(display_columns) > 0: party_title = getattr(party_model, display_columns[0], None) else: party_title = str(row_number) self.parent.custom_tenure_tab.insertTab( row_number, self.entity_editors[(str_number, row_number)].entity_tab_widget.widget(0), party_title) return True
class STDMFieldWidget: # Instantiate the singleton QgsEditorWidgetRegistry widgetRegistry = QgsGui.editorWidgetRegistry() def __init__(self, plugin): self.entity = None self.widget_mapping = {} self.column_mapping = {} self.layer = None self.feature_models = OrderedDict() self.removed_feature_models = OrderedDict() self.current_feature = None self.editor = None self.plugin = plugin def init_form(self, table, spatial_column, curr_layer): """ Initialize required methods and slots to be used in form initialization. :param table: The table name of the layer :type table: String :param spatial_column: The spatial column name of the layer :type spatial_column: String :param curr_layer: The current layer of form. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneTYpe """ # init form self.set_entity(table) self.set_widget_mapping() self.register_factory() self.set_widget_type(curr_layer) form_config = curr_layer.editFormConfig() form_config.setSuppress(QgsEditFormConfig.SuppressOn) curr_layer.setEditFormConfig(form_config) curr_layer.featureAdded.connect( lambda feature_id: self.load_stdm_form( feature_id, spatial_column ) ) curr_layer.featureDeleted.connect( self.on_feature_deleted ) curr_layer.beforeCommitChanges.connect( self.on_digitizing_saved ) def set_entity(self, source): """ Sets the layer entity of the layer based on a table name. :param source: Table name that acts as a layer source. :type source: String :return: None :rtype: NoneType """ curr_profile = current_profile() self.entity = curr_profile.entity_by_name( source ) def _set_widget_type(self, layer, column, widget_type_id): """ Sets the widget type for each field into QGIS form configuration. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :param column: STDM column object :type column: Object :param widget_type_id: The widget type id which could be the default QGIS or the custom STDM widget id which is based on column.TYPE_INFO. :type widget_type_id: String :return: None :rtype:NoneType """ idx = layer.fields().indexFromName(column.name) # Set Alias/ Display names for the column names layer.setFieldAlias( idx, column.header() ) setup = QgsEditorWidgetSetup(widget_type_id, {}) layer.setEditorWidgetSetup(idx, setup) def set_widget_mapping(self): """ Maps each column to QGIS or STDM editor widgets. :return: None :rtype:NoneType """ self.widget_mapping.clear() self.column_mapping.clear() for c in self.entity.columns.values(): self.column_mapping[c.name] = c if c.TYPE_INFO == 'SERIAL': self.widget_mapping[c.name] = ['Hidden', None] elif c.TYPE_INFO == 'GEOMETRY': self.widget_mapping[c.name] = ['TextEdit', None] else: stdm = QApplication.translate( 'STDMFieldWidget', 'STDM' ) self.widget_mapping[c.name] = [ 'stdm_{}'.format( c.TYPE_INFO.lower() ), '{} {}'.format( stdm, c.display_name() ) ] def register_factory(self): """ Registers each widget type to a QGIS widget factory registry. :return: None :rtype: NoneType """ # The destructor has no effects. It is QGIS bug. # So restarting QGIS is required to destroy # registered stdm widgets. for widget_id_name in self.widget_mapping.values(): # add and register stdm widget type only if not widget_id_name[1] is None: widget_name = widget_id_name[1] if widget_id_name[0] not in \ self.widgetRegistry.factories().keys(): widget_factory = QGISFieldWidgetFactory( widget_name ) self.widgetRegistry.registerWidget( widget_id_name[0], widget_factory ) def set_widget_type(self, layer): """ Sets widget type for each fields in a layer. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :return: None :rtype: NoneType """ self.layer = layer for col_name, widget_id_name in \ self.widget_mapping.items(): col = self.column_mapping[col_name] self._set_widget_type( layer, col, widget_id_name[0] ) def feature_to_model(self, feature_id): """ Converts feature to db model. :param feature_id: The feature id :type feature_id: Integer :return: The model and number of columns with data. :rtype: Tuple """ ent_model = entity_model(self.entity) model_obj = ent_model() iterator = self.layer.getFeatures( QgsFeatureRequest().setFilterFid(feature_id)) feature = next(iterator) field_names = [field.name() for field in self.layer.fields()] attribute = feature.attributes() if isinstance(attribute[0], QgsField): return None, 0 mapped_data = OrderedDict(list(zip(field_names, feature.attributes()))) col_with_data = [] for col, value in mapped_data.items(): if col == 'id': continue if value is None: continue if value == NULL: continue setattr(model_obj, col, value) col_with_data.append(col) return model_obj, len(col_with_data) def load_stdm_form(self, feature_id, spatial_column): """ Loads STDM Form and collects the model added into the form so that it is saved later. :param feature_id: the ID of a feature that is last added :type feature_id: Integer :param spatial_column: The spatial column name of the layer :type spatial_column: String :return: None :rtype:NoneType """ srid = None self.current_feature = feature_id # If the digitizing save button is clicked, # the featureAdded signal is called but the # feature ids value is over 0. Return to prevent # the dialog from popping up for every feature. if feature_id > 0: return # if the feature is already in the OrderedDict don't # show the form as the model of the feature is # already populated by the form if feature_id in self.feature_models.keys(): return # If the feature is removed by the undo button, don't # load the form for it but add it # back to feature_models and don't show the form. # This happens when redo button(add feature back) is # clicked after an undo button(remove feature) if feature_id in self.removed_feature_models.keys(): self.feature_models[feature_id] = \ self.removed_feature_models[feature_id] return # If the feature is not valid, geom_wkt will be None # So don't launch form for invalid feature and delete feature geom_wkt = self.get_wkt(spatial_column, feature_id) if geom_wkt is None: title = QApplication.translate( 'STDMFieldWidget', 'Spatial Entity Form Error', None ) msg = QApplication.translate( 'STDMFieldWidget', 'The feature you have added is invalid. \n' 'To fix this issue, check if the feature ' 'is digitized correctly. \n' 'Make sure you have added a base layer to digitize on.', None ) # Message: Spatial column information # could not be found QMessageBox.critical( iface.mainWindow(), title, msg ) return # init form feature_model, col_with_data = self.feature_to_model(feature_id) if col_with_data == 0: feature_model = None self.editor = EntityEditorDialog( self.entity, model=feature_model, parent=iface.mainWindow(), manage_documents=True, collect_model=True, plugin=self.plugin ) self.model = self.editor.model() self.editor.addedModel.connect(self.on_form_saved) # get srid with EPSG text full_srid = self.layer.crs().authid().split(':') if len(full_srid) > 0: # Only extract the number srid = full_srid[1] if geom_wkt is not None: # add geometry into the model setattr( self.model, spatial_column, 'SRID={};{}'.format(srid, geom_wkt) ) # open editor result = self.editor.exec_() if result < 1: self.removed_feature_models[feature_id] = None self.layer.deleteFeature(feature_id) def get_wkt(self, spatial_column, feature_id): """ Gets feature geometry in Well-Known Text format and returns it. :param spatial_column: The spatial column name. :type spatial_column: String :param feature_id: Feature id :type feature_id: Integer :return: Well-Known Text format of a geometry :rtype: WKT """ geom_wkt = None fid = feature_id request = QgsFeatureRequest() request.setFilterFid(fid) features = self.layer.getFeatures(request) geom_col_obj = self.entity.columns[spatial_column] geom_type = geom_col_obj.geometry_type() # get the wkt of the geometry for feature in features: geometry = feature.geometry() if geometry.isGeosValid(): if geom_type in ['MULTIPOLYGON', 'MULTILINESTRING']: geometry.convertToMultiType() geom_wkt = geometry.asWkt() return geom_wkt def on_form_saved(self, model): """ A slot raised when the save button is clicked in spatial unit form. It adds the feature model in feature_models ordered dictionary to be saved later. :param model: The model holding feature geometry and attributes obtained from the form :type model: SQLAlchemy Model :return: None :rtype: NoneType """ if model is not None: self.feature_models[self.current_feature] = model if self.editor.is_valid: self.editor.accept() def on_feature_deleted(self, feature_id): """ A slot raised when a feature is deleted in QGIS map canvas via the undo button. It deletes the associated model of the feature. :param feature_id: The id that is removed. :type feature_id: Integer :return: None :rtype: NoneType """ if feature_id in self.feature_models.keys(): self.removed_feature_models[feature_id] = \ self.feature_models[feature_id] del self.feature_models[feature_id] def on_digitizing_saved(self): """ A slot raised when the save button is clicked on Digitizing Toolbar of QGIS. It saves feature models created by the digitizer and STDM form to the Database. :return: None :rtype: NoneType """ ent_model = entity_model(self.entity) entity_obj = ent_model() entity_obj.saveMany( list(self.feature_models.values()) ) for model in self.feature_models.values(): STDMDb.instance().session.flush() for attrMapper in self.editor._attrMappers: control = attrMapper.valueHandler().control if isinstance(control, ExpressionLineEdit): value = control.on_expression_triggered(model) print(attrMapper._attrName, value) setattr(model, attrMapper._attrName, value) model.update() # Save child models if self.editor is not None: self.editor.save_children() # undo each feature created so that qgis # don't try to save the same feature again. # It will also clear all the models from # self.feature_models as on_feature_deleted # is raised when a feature is removed. feature_ids_to_delete = list(self.feature_models.keys()) for f_id in feature_ids_to_delete: iface.mainWindow().blockSignals(True) self.layer.deleteFeature(f_id) self.on_feature_deleted(f_id) iface.mainWindow().blockSignals(True) for i in range(len(self.feature_models)): self.layer.undoStack().undo()
def load_stdm_form(self, feature_id, spatial_column): """ Loads STDM Form and collects the model added into the form so that it is saved later. :param feature_id: the ID of a feature that is last added :type feature_id: Integer :param spatial_column: The spatial column name of the layer :type spatial_column: String :return: None :rtype:NoneType """ srid = None self.current_feature = feature_id # If the digitizing save button is clicked, # the featureAdded signal is called but the # feature ids value is over 0. Return to prevent # the dialog from popping up for every feature. if feature_id > 0: return # if the feature is already in the OrderedDict don't # show the form as the model of the feature is # already populated by the form if feature_id in self.feature_models.keys(): return # If the feature is removed by the undo button, don't # load the form for it but add it # back to feature_models and don't show the form. # This happens when redo button(add feature back) is # clicked after an undo button(remove feature) if feature_id in self.removed_feature_models.keys(): self.feature_models[feature_id] = \ self.removed_feature_models[feature_id] return # If the feature is not valid, geom_wkt will be None # So don't launch form for invalid feature and delete feature geom_wkt = self.get_wkt(spatial_column, feature_id) if geom_wkt is None: title = QApplication.translate( 'STDMFieldWidget', u'Spatial Entity Form Error', None, QCoreApplication.UnicodeUTF8 ) msg = QApplication.translate( 'STDMFieldWidget', u'The feature you have added is invalid. \n' 'To fix this issue, check if the feature ' 'is digitized correctly. \n' 'Make sure you have added a base layer to digitize on.', None, QCoreApplication.UnicodeUTF8 ) # Message: Spatial column information # could not be found QMessageBox.critical( iface.mainWindow(), title, msg ) return # init form feature_model, col_with_data = self.feature_to_model(feature_id) if col_with_data == 0: feature_model = None self.editor = EntityEditorDialog( self.entity, model=feature_model, parent=iface.mainWindow(), manage_documents=True, collect_model=True, plugin=self.plugin ) self.model = self.editor.model() self.editor.addedModel.connect(self.on_form_saved) # get srid with EPSG text full_srid = self.layer.crs().authid().split(':') if len(full_srid) > 0: # Only extract the number srid = full_srid[1] if not geom_wkt is None: # add geometry into the model setattr( self.model, spatial_column, 'SRID={};{}'.format(srid, geom_wkt) ) # open editor result = self.editor.exec_() if result < 1: self.removed_feature_models[feature_id] = None self.layer.deleteFeature(feature_id)
class STDMFieldWidget(): # Instantiate the singleton QgsEditorWidgetRegistry widgetRegistry = QgsEditorWidgetRegistry.instance() def __init__(self, plugin): self.entity = None self.widget_mapping = {} self.layer = None self.feature_models = OrderedDict() self.removed_feature_models = OrderedDict() self.current_feature = None self.editor = None self.plugin = plugin def init_form(self, table, spatial_column, curr_layer): """ Initialize required methods and slots to be used in form initialization. :param table: The table name of the layer :type table: String :param spatial_column: The spatial column name of the layer :type spatial_column: String :param curr_layer: The current layer of form. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneTYpe """ try: # init form self.set_entity(table) self.set_widget_mapping() self.register_factory() self.set_widget_type(curr_layer) curr_layer.editFormConfig().setSuppress(1) try: curr_layer.featureAdded.connect( lambda feature_id: self.load_stdm_form( feature_id, spatial_column ) ) except Exception: pass curr_layer.featureDeleted.connect( self.on_feature_deleted ) curr_layer.beforeCommitChanges.connect( self.on_digitizing_saved ) except Exception as ex: LOGGER.debug(ex) def set_entity(self, source): """ Sets the layer entity of the layer based on a table name. :param source: Table name that acts as a layer source. :type source: String :return: None :rtype: NoneType """ curr_profile = current_profile() self.entity = curr_profile.entity_by_name( source ) def _set_widget_type(self, layer, column, widget_type_id): """ Sets the widget type for each field into QGIS form configuration. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :param column: STDM column object :type column: Object :param widget_type_id: The widget type id which could be the default QGIS or the custom STDM widget id which is based on column.TYPE_INFO. :type widget_type_id: String :return: None :rtype:NoneType """ idx = layer.fieldNameIndex(column.name) # Set Alias/ Display names for the column names layer.addAttributeAlias( idx, column.header() ) try: layer.editFormConfig().setWidgetType( idx, widget_type_id ) except Exception: layer.setEditorWidgetV2( idx, widget_type_id ) def set_widget_mapping(self): """ Maps each column to QGIS or STDM editor widgets. :return: None :rtype:NoneType """ self.widget_mapping.clear() for c in self.entity.columns.values(): if c.TYPE_INFO == 'SERIAL': self.widget_mapping[c] = ['Hidden', None] elif c.TYPE_INFO == 'GEOMETRY': self.widget_mapping[c] = ['TextEdit', None] else: stdm = QApplication.translate( 'STDMFieldWidget', u'STDM' ) self.widget_mapping[c] = [ u'stdm_{}'.format( c.TYPE_INFO.lower() ), u'{} {}'.format( stdm, c.display_name() ) ] def register_factory(self): """ Registers each widget type to a QGIS widget factory registry. :return: None :rtype: NoneType """ # The destructor has no effects. It is QGIS bug. # So restarting QGIS is required to destroy # registered stdm widgets. for widget_id_name in self.widget_mapping.values(): # add and register stdm widget type only if not widget_id_name[1] is None: widget_name = widget_id_name[1] if widget_id_name[0] not in \ self.widgetRegistry.factories().keys(): widget_factory = QGISFieldWidgetFactory( widget_name ) self.widgetRegistry.registerWidget( widget_id_name[0], widget_factory ) def set_widget_type(self, layer): """ Sets widget type for each fields in a layer. :param layer: The layer to which the widget type is set. :type layer: QgsVectorLayer :return: None :rtype: NoneType """ self.layer = layer for col, widget_id_name in \ self.widget_mapping.iteritems(): self._set_widget_type( layer, col, widget_id_name[0] ) def feature_to_model(self, feature_id): """ Converts feature to db model. :param feature_id: The feature id :type feature_id: Integer :return: The model and number of columns with data. :rtype: Tuple """ ent_model = entity_model(self.entity) model_obj = ent_model() iterator = self.layer.getFeatures( QgsFeatureRequest().setFilterFid(feature_id)) feature = next(iterator) field_names = [field.name() for field in self.layer.pendingFields()] attribute = feature.attributes() if isinstance(attribute[0], QgsField): return None, 0 mapped_data = OrderedDict(zip(field_names, feature.attributes())) col_with_data = [] for col, value in mapped_data.iteritems(): if col == 'id': continue if value is None: continue if value == NULL: continue setattr(model_obj, col, value) col_with_data.append(col) return model_obj, len(col_with_data) def load_stdm_form(self, feature_id, spatial_column): """ Loads STDM Form and collects the model added into the form so that it is saved later. :param feature_id: the ID of a feature that is last added :type feature_id: Integer :param spatial_column: The spatial column name of the layer :type spatial_column: String :return: None :rtype:NoneType """ srid = None self.current_feature = feature_id # If the digitizing save button is clicked, # the featureAdded signal is called but the # feature ids value is over 0. Return to prevent # the dialog from popping up for every feature. if feature_id > 0: return # if the feature is already in the OrderedDict don't # show the form as the model of the feature is # already populated by the form if feature_id in self.feature_models.keys(): return # If the feature is removed by the undo button, don't # load the form for it but add it # back to feature_models and don't show the form. # This happens when redo button(add feature back) is # clicked after an undo button(remove feature) if feature_id in self.removed_feature_models.keys(): self.feature_models[feature_id] = \ self.removed_feature_models[feature_id] return # If the feature is not valid, geom_wkt will be None # So don't launch form for invalid feature and delete feature geom_wkt = self.get_wkt(spatial_column, feature_id) if geom_wkt is None: title = QApplication.translate( 'STDMFieldWidget', u'Spatial Entity Form Error', None, QCoreApplication.UnicodeUTF8 ) msg = QApplication.translate( 'STDMFieldWidget', u'The feature you have added is invalid. \n' 'To fix this issue, check if the feature ' 'is digitized correctly. \n' 'Make sure you have added a base layer to digitize on.', None, QCoreApplication.UnicodeUTF8 ) # Message: Spatial column information # could not be found QMessageBox.critical( iface.mainWindow(), title, msg ) return # init form feature_model, col_with_data = self.feature_to_model(feature_id) if col_with_data == 0: feature_model = None self.editor = EntityEditorDialog( self.entity, model=feature_model, parent=iface.mainWindow(), manage_documents=True, collect_model=True, plugin=self.plugin ) self.model = self.editor.model() self.editor.addedModel.connect(self.on_form_saved) # get srid with EPSG text full_srid = self.layer.crs().authid().split(':') if len(full_srid) > 0: # Only extract the number srid = full_srid[1] if not geom_wkt is None: # add geometry into the model setattr( self.model, spatial_column, 'SRID={};{}'.format(srid, geom_wkt) ) # open editor result = self.editor.exec_() if result < 1: self.removed_feature_models[feature_id] = None self.layer.deleteFeature(feature_id) def get_wkt(self, spatial_column, feature_id): """ Gets feature geometry in Well-Known Text format and returns it. :param spatial_column: The spatial column name. :type spatial_column: String :param feature_id: Feature id :type feature_id: Integer :return: Well-Known Text format of a geometry :rtype: WKT """ geom_wkt = None fid = feature_id request = QgsFeatureRequest() request.setFilterFid(fid) features = self.layer.getFeatures(request) geom_col_obj = self.entity.columns[spatial_column] geom_type = geom_col_obj.geometry_type() # get the wkt of the geometry for feature in features: geometry = feature.geometry() if geometry.isGeosValid(): if geom_type in ['MULTIPOLYGON', 'MULTILINESTRING']: geometry.convertToMultiType() geom_wkt = geometry.exportToWkt() return geom_wkt def on_form_saved(self, model): """ A slot raised when the save button is clicked in spatial unit form. It adds the feature model in feature_models ordered dictionary to be saved later. :param model: The model holding feature geometry and attributes obtained from the form :type model: SQLAlchemy Model :return: None :rtype: NoneType """ if not model is None: self.feature_models[self.current_feature] = model if self.editor.is_valid: self.editor.accept() def on_feature_deleted(self, feature_id): """ A slot raised when a feature is deleted in QGIS map canvas via the undo button. It deletes the associated model of the feature. :param feature_id: The id that is removed. :type feature_id: Integer :return: None :rtype: NoneType """ if feature_id in self.feature_models.keys(): self.removed_feature_models[feature_id] = \ self.feature_models[feature_id] del self.feature_models[feature_id] def on_digitizing_saved(self): """ A slot raised when the save button is clicked on Digitizing Toolbar of QGIS. It saves feature models created by the digitizer and STDM form to the Database. :return: None :rtype: NoneType """ ent_model = entity_model(self.entity) entity_obj = ent_model() entity_obj.saveMany( self.feature_models.values() ) for model in self.feature_models.values(): STDMDb.instance().session.flush() for attrMapper in self.editor._attrMappers: control = attrMapper.valueHandler().control if isinstance(control, ExpressionLineEdit): value = control.on_expression_triggered(model) print attrMapper._attrName, value setattr(model, attrMapper._attrName, value) model.update() # Save child models if self.editor is not None: self.editor.save_children() # undo each feature created so that qgis # don't try to save the same feature again. # It will also clear all the models from # self.feature_models as on_feature_deleted # is raised when a feature is removed. for f_id in self.feature_models.keys(): iface.mainWindow().blockSignals(True) self.layer.deleteFeature(f_id) self.on_feature_deleted(f_id) iface.mainWindow().blockSignals(True) for i in range(len(self.feature_models)): self.layer.undoStack().undo()
def __init__(self, entity, parent): EntityEditorDialog.__init__(self, entity, parent=parent) self.parent = parent