class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet("min-height: 150px") self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[str].connect(self.pFilterModel.setFilterFixedString)
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet('min-height: 150px') self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[unicode].connect( self.pFilterModel.setFilterFixedString)
def set_completer_lineedit(qlineedit, list_items): """ Set a completer into a QLineEdit :param qlineedit: Object where to set the completer (QLineEdit) :param list_items: List of items to set into the completer (List)["item1","item2","..."] """ completer = QCompleter() completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setMaxVisibleItems(10) completer.setCompletionMode(0) completer.setFilterMode(Qt.MatchContains) completer.popup().setStyleSheet("color: black;") qlineedit.setCompleter(completer) model = QStringListModel() model.setStringList(list_items) completer.setModel(model)
def set_model_by_list(string_list, widget, proxy_model): """ Set the model according to the list """ model = QStringListModel() model.setStringList(string_list) proxy_model.setSourceModel(model) proxy_model.setFilterKeyColumn(0) proxy_model_aux = QSortFilterProxyModel() proxy_model_aux.setSourceModel(model) proxy_model_aux.setFilterKeyColumn(0) widget.setModel(proxy_model_aux) widget.setModelColumn(0) completer = QCompleter() completer.setModel(proxy_model) completer.setCompletionColumn(0) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) widget.setCompleter(completer)
def _update_completer(self, values): # Get the items in a tuple and put them in a list # Store display and actual values in a # model for easier mapping and # retrieval when carrying out searches model_attr_mapping = [] # Check if there are formaters specified # for the current field name for mv in values: f_model_values = [] m_val = mv[0] if m_val is not None: col_label = self.currentFieldName() if col_label in self.config.LookupFormatters: formatter = self.config.LookupFormatters[col_label] if formatter.column.TYPE_INFO == 'LOOKUP': m_val = formatter.code_value(m_val)[0] else: m_val = formatter.format_column_value(m_val) f_model_values.extend([m_val, m_val]) model_attr_mapping.append(f_model_values) self._completer_model = BaseSTDMTableModel(model_attr_mapping, ["", ""], self) # We will use the QSortFilterProxyModel for filtering purposes self._proxy_completer_model = QSortFilterProxyModel() self._proxy_completer_model.setDynamicSortFilter(True) self._proxy_completer_model.setSourceModel(self._completer_model) self._proxy_completer_model.setSortCaseSensitivity(Qt.CaseInsensitive) self._proxy_completer_model.setFilterKeyColumn(0) # Configure completer mod_completer = QCompleter(self._completer_model, self) mod_completer.setCaseSensitivity(Qt.CaseInsensitive) mod_completer.setCompletionMode(QCompleter.PopupCompletion) mod_completer.setCompletionColumn(0) mod_completer.setCompletionRole(Qt.DisplayRole) self.txtFilterPattern.setCompleter(mod_completer)
class ExtendedComboBox(QComboBox): """Extended class of QComboBox so we can perform a filtering of items. """ def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[str].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # on selection of an item from the completer, # select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of # the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column)
class FilteredComboBox(QComboBox): """Custom QComboBox with filtering option.""" def __init__(self, parent=None): super(FilteredComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) self.setEditable(True) self.filter_proxy_model = QSortFilterProxyModel(self) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSourceModel(self.model()) self.completer = QCompleter(self.filter_proxy_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.setMinimumSize(150, 25) self.setFont(QFont("Segoe UI", 10)) self.setStyleSheet("QComboBox {background-color: white;}") self.setMaxVisibleItems(10) self.completer.activated.connect(self.on_completer_activated) self.lineEdit().textEdited.connect( self.filter_proxy_model.setFilterFixedString) def on_completer_activated(self, text): """Set active combobox item when a completer item is picked.""" if not text: return idx = self.findText(text) self.setCurrentIndex(idx) self.activated[str].emit(self.itemText(idx)) def setModel(self, model): """Set completer model after the combobox model.""" super(FilteredComboBox, self).setModel(model) self.filter_proxy_model.setSourceModel(model) self.completer.setModel(self.filter_proxy_model) def setModelColumn(self, column_idx): """Set the correct column for completer and combobox model using column index.""" self.completer.setCompletionColumn(column_idx) self.filter_proxy_model.setFilterKeyColumn(column_idx) super(FilteredComboBox, self).setModelColumn(column_idx)
class DeletionReason(QDialog, FORM_CLASS): def __init__(self, selected_number, parent=None): super(DeletionReason, self).__init__(parent) self.setupUi(self) self.setWindowModality(Qt.ApplicationModal) self.db = db # initiate label self.lb_reason.setText( "Number of outlines that will be deleted: {}".format( selected_number)) # initiate le_deletion_reason self.le_reason.setMaxLength(250) self.le_reason.setPlaceholderText("Reason for Deletion") self.completer_box() def completer_box(self): """Box automatic completion""" reasons = self.db._execute(bulk_load_select.deletion_description_value) reason_list = [row[0] for row in reasons.fetchall()] # Fill the search box self.completer = QCompleter(reason_list) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.le_reason.setCompleter(self.completer)
def finished(self, result): if result == True: self.dlg.layerconcepts.clear() self.dlg.comboBox.setCurrentIndex(0) self.maindlg.currentgraph = self.graph self.dlg.layercount.setText("[" + str(len(self.geoconcepts)) + "]") for geo in self.geoconcepts: self.dlg.layerconcepts.addItem(geo) comp = QCompleter(self.dlg.layerconcepts) comp.setCompletionMode(QCompleter.PopupCompletion) comp.setModel(self.dlg.layerconcepts.model()) self.dlg.layerconcepts.setCompleter(comp) self.dlg.inp_sparql2.setPlainText( self.triplestoreconf[0]["querytemplate"][0]["query"].replace( "%%concept%%", self.geoconcepts[0])) self.dlg.inp_sparql2.columnvars = {} self.maindlg.loadedfromfile = True self.maindlg.justloadingfromfile = False self.loadgraphdlg.close() else: msgBox = QMessageBox() msgBox.setText(self.exception) msgBox.exec() self.progress.close()
class EditDialog(QDialog, FORM_CLASS): """ Dialog to edit building outlines""" edit_geometry_saved = pyqtSignal(list) delete_outline_saved = pyqtSignal(list, str) def __init__(self, parent_frame, parent=None): super(EditDialog, self).__init__(parent) self.setupUi(self) self.setWindowModality(Qt.NonModal) self.setWindowFlags(Qt.WindowStaysOnTopHint) self.parent_frame = parent_frame self.layer_registry = self.parent_frame.layer_registry self.db = self.parent_frame.db self.parent_frame_name = self.parent_frame.__class__.__name__ if self.parent_frame_name == "BulkLoadFrame": self.editing_layer = self.parent_frame.bulk_load_layer self.current_dataset = self.parent_frame.current_dataset # Update qa layers self.edit_geometry_saved.connect(self.liqa_on_edit_geometry_saved) self.delete_outline_saved.connect(self.liqa_on_delete_outline_saved) elif self.parent_frame_name == "AlterRelationships": self.editing_layer = self.parent_frame.lyr_bulk_load self.current_dataset = self.parent_frame.current_dataset elif self.parent_frame_name == "ProductionFrame": self.editing_layer = self.parent_frame.building_layer self.current_dataset = None self.init_dialog() # Bulk loadings & editing fields self.added_geoms = OrderedDict() self.geom = None self.ids = [] self.geoms = {} self.bulk_load_outline_id = None self.split_geoms = {} # processing class instances self.change_instance = None # initiate le_deletion_reason self.le_deletion_reason.setMaxLength(250) self.le_deletion_reason.setPlaceholderText("Reason for Deletion") self.completer_box() self.cmb_status.currentIndexChanged.connect(self.enable_le_deletion_reason) self.rejected.connect(self.close_dialog) def init_dialog(self): """Constructor """ self.layout_status.hide() self.layout_capture_method.hide() self.layout_lifecycle_stage.hide() self.layout_general_info.hide() self.layout_end_lifespan.hide() self.cmb_status.setDisabled(1) self.le_deletion_reason.setDisabled(1) self.cmb_capture_method.setDisabled(1) self.cmb_lifecycle_stage.setDisabled(1) self.cmb_capture_source.setDisabled(1) self.cmb_ta.setDisabled(1) self.cmb_town.setDisabled(1) self.cmb_suburb.setDisabled(1) self.btn_edit_save.setDisabled(1) self.btn_edit_reset.setDisabled(1) self.btn_end_lifespan.setDisabled(1) def add_outline(self): """When the user selects to add a new outline""" self.setWindowTitle("Add Outline") self.added_geoms = OrderedDict() self.geom = None iface.actionCancelEdits().trigger() # reset toolbar for action in iface.building_toolbar.actions(): if action.text() not in ["Pan Map", "Add Outline", "Edit Geometry", "Edit Attributes"]: iface.building_toolbar.removeAction(action) if action.text() == "Add Outline": action.setDisabled(True) else: action.setEnabled(True) # set change instance to added class try: self.btn_edit_save.clicked.disconnect() except TypeError: pass try: self.btn_edit_reset.clicked.disconnect() except TypeError: pass if self.parent_frame_name == "BulkLoadFrame" or self.parent_frame_name == "AlterRelationships": self.change_instance = bulk_load_changes.AddBulkLoad(self) self.layout_status.hide() self.layout_capture_method.show() self.layout_lifecycle_stage.hide() self.layout_general_info.show() self.layout_end_lifespan.hide() elif self.parent_frame_name == "ProductionFrame": self.change_instance = production_changes.AddProduction(self) self.layout_status.hide() self.layout_capture_method.show() self.layout_lifecycle_stage.show() self.layout_general_info.show() self.layout_end_lifespan.hide() # connect signals and slots self.btn_edit_save.clicked.connect(partial(self.change_instance.edit_save_clicked, True)) self.btn_edit_reset.clicked.connect(self.change_instance.edit_reset_clicked) self.editing_layer.featureAdded.connect(self.change_instance.creator_feature_added) self.editing_layer.featureDeleted.connect(self.change_instance.creator_feature_deleted) self.editing_layer.geometryChanged.connect(self.change_instance.creator_geometry_changed) def edit_attribute(self): """When the user selects to edit a building attribute""" self.setWindowTitle("Edit Attribute") self.ids = [] self.building_outline_id = None iface.actionCancelEdits().trigger() # reset toolbar for action in iface.building_toolbar.actions(): if action.text() not in ["Pan Map", "Add Outline", "Edit Geometry", "Edit Attributes"]: iface.building_toolbar.removeAction(action) if action.text() in ["Edit Attributes"]: action.setDisabled(True) else: action.setEnabled(True) try: self.btn_edit_save.clicked.disconnect() except TypeError: pass try: self.btn_edit_reset.clicked.disconnect() except TypeError: pass if self.parent_frame_name == "BulkLoadFrame" or self.parent_frame_name == "AlterRelationships": self.change_instance = bulk_load_changes.EditAttribute(self) self.layout_status.show() self.layout_capture_method.show() self.layout_lifecycle_stage.hide() self.layout_general_info.show() self.layout_end_lifespan.hide() elif self.parent_frame_name == "ProductionFrame": self.change_instance = production_changes.EditAttribute(self) self.layout_status.hide() self.layout_capture_method.show() self.layout_lifecycle_stage.show() self.layout_general_info.show() self.layout_end_lifespan.show() try: self.btn_end_lifespan.clicked.disconnect() except Exception: pass self.btn_end_lifespan.clicked.connect(partial(self.change_instance.end_lifespan, True)) # set up signals and slots self.btn_edit_save.clicked.connect(partial(self.change_instance.edit_save_clicked, True)) self.btn_edit_reset.clicked.connect(self.change_instance.edit_reset_clicked) self.editing_layer.selectionChanged.connect(self.change_instance.selection_changed) def edit_geometry(self): """"When the user selects to edit a building geometry""" self.setWindowTitle("Edit Geometry") self.geoms = {} iface.actionCancelEdits().trigger() # reset toolbar for action in iface.building_toolbar.actions(): if action.text() not in ["Pan Map", "Add Outline", "Edit Geometry", "Edit Attributes"]: iface.building_toolbar.removeAction(action) if action.text() == "Edit Geometry": action.setDisabled(True) else: action.setEnabled(True) try: self.btn_edit_save.clicked.disconnect() except TypeError: pass try: self.btn_edit_reset.clicked.disconnect() except TypeError: pass if self.parent_frame_name == "BulkLoadFrame" or self.parent_frame_name == "AlterRelationships": self.change_instance = bulk_load_changes.EditGeometry(self) self.layout_status.hide() self.layout_capture_method.show() self.layout_lifecycle_stage.hide() self.layout_general_info.hide() self.layout_end_lifespan.hide() elif self.parent_frame_name == "ProductionFrame": self.change_instance = production_changes.EditGeometry(self) self.layout_status.hide() self.layout_capture_method.show() self.layout_lifecycle_stage.hide() self.layout_general_info.hide() self.layout_end_lifespan.hide() # set up signals and slots self.btn_edit_save.clicked.connect(partial(self.change_instance.edit_save_clicked, True)) self.btn_edit_reset.clicked.connect(self.change_instance.edit_reset_clicked) self.editing_layer.geometryChanged.connect(self.change_instance.geometry_changed) self.editing_layer.featureAdded.connect(self.change_instance.creator_feature_added) def close_dialog(self): """When 'x' is clicked""" self.change_instance = None self.added_geoms = OrderedDict() self.geom = None self.ids = [] self.building_outline_id = None self.geoms = {} self.split_geoms = {} self.added_building_ids = [] self.parent_frame.edit_cancel_clicked() for action in iface.building_toolbar.actions(): if action.text() not in ["Pan Map", "Add Outline", "Edit Geometry", "Edit Attributes"]: iface.building_toolbar.removeAction(action) else: action.setEnabled(True) def get_change_instance(self): """Return change instance""" return self.change_instance def completer_box(self): """Box automatic completion""" reasons = self.db.execute_return(bulk_load_select.deletion_description_value) reason_list = [row[0] for row in reasons.fetchall()] # Fill the search box self.completer = QCompleter(reason_list) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.le_deletion_reason.setCompleter(self.completer) @pyqtSlot() def enable_le_deletion_reason(self): """When the user opts to delete an outline""" if self.cmb_status.currentText() == "Deleted During QA": self.le_deletion_reason.setEnabled(1) self.le_deletion_reason.setFocus() self.le_deletion_reason.selectAll() else: self.le_deletion_reason.setDisabled(1) self.le_deletion_reason.clear() @pyqtSlot(list) def liqa_on_edit_geometry_saved(self, ids): """Update LIQA when geometry edited""" for qa_lyr in self.find_qa_layer(): if not self.bulk_load_id_field_exists(qa_lyr): continue bulk_load_ids = self.get_bulk_load_ids(qa_lyr) for feat_id in ids: if feat_id in list(bulk_load_ids.values()): qa_feat_id = list(bulk_load_ids.keys())[list(bulk_load_ids.values()).index(feat_id)] self.update_qa_layer_attribute(qa_lyr, qa_feat_id, "Fixed", "Geometry edited") @pyqtSlot(list, str) def liqa_on_delete_outline_saved(self, ids, del_reason): """Update LIQA when feature deleted""" for qa_lyr in self.find_qa_layer(): if not self.bulk_load_id_field_exists(qa_lyr): continue bulk_load_ids = self.get_bulk_load_ids(qa_lyr) for feat_id in ids: if feat_id in list(bulk_load_ids.values()): qa_feat_id = list(bulk_load_ids.keys())[list(bulk_load_ids.values()).index(feat_id)] self.update_qa_layer_attribute(qa_lyr, qa_feat_id, "Fixed", "Deleted- {}".format(del_reason)) def bulk_load_id_field_exists(self, qa_layer): field_names = [field.name() for field in qa_layer.fields()] if "bulk_load_" in field_names: return True return False def find_qa_layer(self): """find qa layer""" for layer in QgsProject.instance().mapLayers().values(): if layer.name().startswith("qa_"): yield layer def get_bulk_load_ids(self, qa_layer): """return bulk load ids""" bulk_load_ids = {} for feat in qa_layer.getFeatures(): bulk_load_ids[feat.id()] = feat["bulk_load_"] return bulk_load_ids def update_qa_layer_attribute(self, qa_lyr, qa_id, error_status, comment): """update qa layer attributes""" qa_lyr.startEditing() qa_lyr.changeAttributeValue(qa_id, 1, error_status, True) qa_lyr.changeAttributeValue(qa_id, 2, comment, True) qa_lyr.commitChanges()
class ExtendedCombobox(QComboBox): """ Overwrite combobox to provide text filtering of combobox list. """ def __init__(self, parent): """ Initialise ExtendedCombobox :param parent: Parent of combobox :type parent: PyQt5.QtWidgets.QWidget """ super().__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.p_filter_model = QSortFilterProxyModel(self) self.p_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect(self.p_filter_model.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) def setModel(self, model): # pylint:disable=invalid-name """ Set the model to use the Filter model :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ super().setModel(model) self.p_filter_model.setSourceModel(model) self.completer.setModel(self.p_filter_model) def setModelColumn(self, column): # pylint:disable=invalid-name """ :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ self.completer.setCompletionColumn(column) self.p_filter_model.setFilterKeyColumn(column) super().setModelColumn(column) def view(self): """ A QListView of items stored in the model :return: items stored in the model :rtype: PyQt5.QtWidgets.QListView """ return self.completer.popup() def index(self): """ Index of the current item in the combobox. :return: index of the current item :rtype: int """ return self.currentIndex() def setTextIfCompleterIsClicked(self, text): # pylint:disable=invalid-name """ :param text: The current text of the qlineedit :type text: str If the combobx lineedit is clicked, set the lineedits current item as the combobox's current item """ if text: index = self.findText(text) self.setCurrentIndex(index)
class MapTilerGeocoderToolbar: def __init__(self, iface): # TODO: We are going to let the user set this up in a future iteration self.iface = iface self.proj = QgsProject.instance() self.toolbar = self.iface.addToolBar(u'MapTiler') self.toolbar.setObjectName(u'MapTiler') # init QCompleter self.completer = QCompleter([]) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setMaxVisibleItems(30) self.completer.setModelSorting(QCompleter.UnsortedModel) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.activated[QModelIndex].connect(self.on_result_clicked) # init LineEdit of searchword self.search_line_edit = QLineEdit() self.search_line_edit.setPlaceholderText('MapTiler Geocoding API') self.search_line_edit.setMaximumWidth(300) self.search_line_edit.setClearButtonEnabled(True) self.search_line_edit.setCompleter(self.completer) self.search_line_edit.textEdited.connect(self.on_searchword_edited) self.search_line_edit.returnPressed.connect( self.on_searchword_returned) self.toolbar.addWidget(self.search_line_edit) # LineEdit edited event def on_searchword_edited(self): model = self.completer.model() model.setStringList([]) self.completer.complete() # LineEdit returned event def on_searchword_returned(self): searchword = self.search_line_edit.text() geojson_dict = self._fetch_geocoding_api(searchword) # always dict is Non when apikey invalid if geojson_dict is None: return self.result_features = geojson_dict['features'] result_list = [] for feature in self.result_features: result_list.append('%s:%s' % (feature['text'], feature['place_name'])) model = self.completer.model() model.setStringList(result_list) self.completer.complete() def _fetch_geocoding_api(self, searchword): # get a center point of MapCanvas center = self.iface.mapCanvas().center() center_as_qgspoint = QgsPoint(center.x(), center.y()) # transform the center point to EPSG:4326 target_crs = QgsCoordinateReferenceSystem('EPSG:4326') transform = QgsCoordinateTransform(self.proj.crs(), target_crs, self.proj) center_as_qgspoint.transform(transform) center_lonlat = [center_as_qgspoint.x(), center_as_qgspoint.y()] # start Geocoding locale = 'en' global_locale = QSettings().value('locale/globalLocale') if global_locale: locale = global_locale[0:2] else: user_locale = QSettings().value('locale/userLocale') if user_locale: locale = user_locale[0:2] geocoder = MapTilerGeocoder(locale) geojson_dict = geocoder.geocoding(searchword, center_lonlat) return geojson_dict def on_result_clicked(self, result_index): # add selected feature to Project selected_feature = self.result_features[result_index.row()] extent_rect = QgsRectangle() current_crs = QgsCoordinateReferenceSystem() bbox = selected_feature.get("bbox") geometry_type = selected_feature.get("geometry", {}).get("type") if bbox: extent_rect = QgsRectangle(bbox[0], bbox[1], bbox[2], bbox[3]) current_crs = QgsCoordinateReferenceSystem("EPSG:4326") elif geometry_type == "GeometryCollection": geometries = selected_feature.get("geometries") if geometries is None: print("invalid GeoJSON") return tmp_geojson = {"type": "Feature", "geometry": geometries[0]} geojson_str = json.dumps(tmp_geojson) vlayer = QgsVectorLayer(geojson_str, 'tmp', 'ogr') extent_rect = vlayer.extent() current_crs = vlayer.sourceCrs() else: geojson_str = json.dumps(selected_feature) vlayer = QgsVectorLayer(geojson_str, 'tmp', 'ogr') extent_rect = vlayer.extent() current_crs = vlayer.sourceCrs() extent_leftbottom = QgsPoint(extent_rect.xMinimum(), extent_rect.yMinimum()) extent_righttop = QgsPoint(extent_rect.xMaximum(), extent_rect.yMaximum()) # transform 2points to project CRS target_crs = self.proj.crs() transform = QgsCoordinateTransform(current_crs, target_crs, self.proj) extent_leftbottom.transform(transform) extent_righttop.transform(transform) # make rectangle same to new extent by transformed 2points extent_rect = QgsRectangle(extent_leftbottom.x(), extent_leftbottom.y(), extent_righttop.x(), extent_righttop.y()) self.iface.mapCanvas().zoomToFeatureExtent(extent_rect)
class TmParentAction(object): def __init__(self, iface, settings, controller, plugin_dir): """ Class constructor """ # Initialize instance attributes self.tree_manage_version = "1.0" self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.dao = self.controller.dao self.schema_name = self.controller.schema_name self.project_type = None self.file_gsw = None self.gsw_settings = None self.lazy_widget = None def set_controller(self, controller): """ Set controller class """ self.controller = controller self.schema_name = self.controller.schema_name def get_plugin_version(self): """ Get plugin version from metadata.txt file """ # Check if metadata file exists metadata_file = os.path.join(self.plugin_dir, 'metadata.txt') if not os.path.exists(metadata_file): message = "Metadata file not found" + metadata_file self.controller.show_warning(message, parameter=metadata_file) return None metadata = configparser.ConfigParser() metadata.read(metadata_file) plugin_version = metadata.get('general', 'version') if plugin_version is None: message = "Plugin version not found" self.controller.show_warning(message) return plugin_version def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: screens = ctypes.windll.user32 screen_x = screens.GetSystemMetrics(78) screen_y = screens.GetSystemMetrics(79) x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") width = self.controller.plugin_settings_value( dialog.objectName() + "_width", dialog.property('width')) height = self.controller.plugin_settings_value( dialog.objectName() + "_height", dialog.property('height')) if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: if int(x) > screen_x: x = int(screen_x) - int(width) if int(y) > screen_y: y = int(screen_y) dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg self.controller.plugin_settings_set_value( dialog.objectName() + "_width", dialog.width()) self.controller.plugin_settings_set_value( dialog.objectName() + "_height", dialog.height()) self.controller.plugin_settings_set_value(dialog.objectName() + "_x", dialog.pos().x() + 8) self.controller.plugin_settings_set_value(dialog.objectName() + "_y", dialog.pos().y() + 31) def open_dialog(self, dlg=None, dlg_name=None, info=True, maximize_button=True, stay_on_top=True): """ Open dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top, maximize/minimize button and information button # if info is True maximize flag will be ignored # To enable maximize button you must set info to False flags = Qt.WindowCloseButtonHint if info: flags |= Qt.WindowSystemMenuHint | Qt.WindowContextHelpButtonHint else: if maximize_button: flags |= Qt.WindowMinMaxButtonsHint if stay_on_top: flags |= Qt.WindowStaysOnTopHint dlg.setWindowFlags(flags) # Open dialog if issubclass(type(dlg), GwDialog): dlg.open() elif issubclass(type(dlg), GwMainWindow): dlg.show() else: print(f"WARNING: dialog type {type(dlg)} is not handled!") dlg.show() def close_dialog(self, dlg=None): """ Close dialog """ try: self.save_settings(dlg) dlg.close() map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.iface.actionPan().trigger() except AttributeError: pass def hide_colums(self, widget, comuns_to_hide): for i in range(0, len(comuns_to_hide)): widget.hideColumn(comuns_to_hide[i]) def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr_filter is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return False, expr return True, expr def set_table_columns(self, dialog, widget, table_name, project_type=None): """ Configuration of tables. Set visibility and width of columns """ widget = utils_giswater.getWidget(dialog, widget) if not widget: return # Set width and alias of visible columns columns_to_delete = [] sql = (f"SELECT column_index, width, alias, status" f" FROM config_client_forms" f" WHERE table_id = '{table_name}'") if project_type is not None: sql += f" AND project_type = '{project_type}' " sql += " ORDER BY column_index" rows = self.controller.get_rows(sql, log_info=False) if not rows: return for row in rows: if not row['status']: columns_to_delete.append(row['column_index'] - 1) else: width = row['width'] if width is not None: widget.setColumnWidth(row['column_index'] - 1, width) widget.model().setHeaderData(row['column_index'] - 1, Qt.Horizontal, row['alias']) widget.model().select() # Delete columns for column in columns_to_delete: widget.hideColumn(column) def set_completer_object(self, tablename, widget, field_search): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ if not widget: return # Set SQL sql = (f"SELECT DISTINCT({field_search})" f" FROM {tablename}" f" ORDER BY {field_search}") rows = self.controller.get_rows(sql) if rows is None: return for i in range(0, len(rows)): aux = rows[i] rows[i] = aux[0] # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(0) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(rows) self.completer.setModel(model) def refresh_map_canvas(self, restore_cursor=False): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() if restore_cursor: self.set_cursor_restore() def set_cursor_restore(self): """ Restore to previous cursors """ QApplication.restoreOverrideCursor() def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def fill_table(self, qtable, table_name, set_edit_triggers=QTableView.NoEditTriggers, expr_filter=None): """ Fill table @widget filtering query by @workcat_id Set a model with selected filter. Attach that model to selected table @setEditStrategy: 0: OnFieldChange 1: OnRowChange 2: OnManualSubmit """ expr = None if expr_filter: # Check expression (is_valid, expr) = self.check_expression(expr_filter) # @UnusedVariable if not is_valid: return expr # Set a model with selected filter expression if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.setSort(0, 0) model.select() # When change some field we need to refresh Qtableview and filter by psector_id qtable.setEditTriggers(set_edit_triggers) # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view if expr: qtable.setModel(model) qtable.model().setFilter(expr_filter) else: qtable.setModel(model) return expr def get_feature_by_id(self, layer, id, field_id): features = layer.getFeatures() for feature in features: if feature[field_id] == id: return feature return False
class ParentAction(object): def __init__(self, iface, settings, controller, plugin_dir): """ Class constructor """ # Initialize instance attributes self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.dao = self.controller.dao self.schema_name = self.controller.schema_name self.project_type = None self.plugin_version = self.get_plugin_version() self.add_layer = AddLayer(iface, settings, controller, plugin_dir) def set_controller(self, controller): """ Set controller class """ self.controller = controller self.schema_name = self.controller.schema_name def open_web_browser(self, dialog, widget=None): """ Display url using the default browser """ if widget is not None: url = utils_giswater.getWidgetText(dialog, widget) if url == 'null': url = 'http://www.giswater.org' else: url = 'http://www.giswater.org' webbrowser.open(url) def get_plugin_version(self): """ Get plugin version from metadata.txt file """ # Check if metadata file exists metadata_file = os.path.join(self.plugin_dir, 'metadata.txt') if not os.path.exists(metadata_file): message = "Metadata file not found" self.controller.show_warning(message, parameter=metadata_file) return None metadata = configparser.ConfigParser() metadata.read(metadata_file) plugin_version = metadata.get('general', 'version') if plugin_version is None: message = "Plugin version not found" self.controller.show_warning(message) return plugin_version def get_file_dialog(self, dialog, widget): """ Get file dialog """ # Check if selected file exists. Set default value if necessary file_path = utils_giswater.getWidgetText(dialog, widget) if file_path is None or file_path == 'null' or not os.path.exists( str(file_path)): folder_path = self.plugin_dir else: folder_path = os.path.dirname(file_path) # Open dialog to select file os.chdir(folder_path) file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.AnyFile) message = "Select file" folder_path, filter_ = file_dialog.getOpenFileName( parent=None, caption=self.controller.tr(message)) if folder_path: utils_giswater.setWidgetText(dialog, widget, str(folder_path)) def get_folder_dialog(self, dialog, widget): """ Get folder dialog """ # Check if selected folder exists. Set default value if necessary folder_path = utils_giswater.getWidgetText(dialog, widget) if folder_path is None or folder_path == 'null' or not os.path.exists( folder_path): folder_path = os.path.expanduser("~") # Open dialog to select folder os.chdir(folder_path) file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.Directory) message = "Select folder" folder_path = file_dialog.getExistingDirectory( parent=None, caption=self.controller.tr(message), directory=folder_path) if folder_path: utils_giswater.setWidgetText(dialog, widget, str(folder_path)) def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") width = self.controller.plugin_settings_value( dialog.objectName() + "_width", dialog.property('width')) height = self.controller.plugin_settings_value( dialog.objectName() + "_height", dialog.property('height')) if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: screens = ctypes.windll.user32 screen_x = screens.GetSystemMetrics(78) screen_y = screens.GetSystemMetrics(79) if int(x) > screen_x: x = int(screen_x) - int(width) if int(y) > screen_y: y = int(screen_y) dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg self.controller.plugin_settings_set_value( dialog.objectName() + "_width", dialog.property('width')) self.controller.plugin_settings_set_value( dialog.objectName() + "_height", dialog.property('height')) self.controller.plugin_settings_set_value(dialog.objectName() + "_x", dialog.pos().x() + 8) self.controller.plugin_settings_set_value(dialog.objectName() + "_y", dialog.pos().y() + 31) def open_dialog(self, dlg=None, dlg_name=None, info=True, maximize_button=True, stay_on_top=True): """ Open dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top, maximize/minimize button and information button # if info is True maximize flag will be ignored # To enable maximize button you must set info to False flags = Qt.WindowCloseButtonHint if info: flags |= Qt.WindowSystemMenuHint | Qt.WindowContextHelpButtonHint else: if maximize_button: flags |= Qt.WindowMinMaxButtonsHint if stay_on_top: flags |= Qt.WindowStaysOnTopHint dlg.setWindowFlags(flags) # Open dialog if issubclass(type(dlg), GwDialog): dlg.open() elif issubclass(type(dlg), GwMainWindow): dlg.show() else: print(f"WARNING: dialog type {type(dlg)} is not handled!") dlg.show() def close_dialog(self, dlg=None): """ Close dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg try: self.save_settings(dlg) dlg.close() map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.iface.actionPan().trigger() except AttributeError: pass except Exception as e: print(type(e).__name__) def multi_row_selector(self, dialog, tableleft, tableright, field_id_left, field_id_right, name='name', hide_left=[ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 ], hide_right=[1, 2, 3], aql=""): """ :param dialog: :param tableleft: Table to consult and load on the left side :param tableright: Table to consult and load on the right side :param field_id_left: ID field of the left table :param field_id_right: ID field of the right table :param name: field name (used in add_lot.py) :param hide_left: Columns to hide from the left table :param hide_right: Columns to hide from the right table :param aql: (add query left) Query added to the left side (used in basic.py def basic_exploitation_selector()) :return: """ # fill QTableView all_rows tbl_all_rows = dialog.findChild(QTableView, "all_rows") tbl_all_rows.setSelectionBehavior(QAbstractItemView.SelectRows) schema_name = self.schema_name.replace('"', '') query_left = f"SELECT * FROM {schema_name}.{tableleft} WHERE {name} NOT IN " query_left += f"(SELECT {tableleft}.{name} FROM {schema_name}.{tableleft}" query_left += f" RIGHT JOIN {schema_name}.{tableright} ON {tableleft}.{field_id_left} = {tableright}.{field_id_right}" query_left += f" WHERE cur_user = current_user)" query_left += f" AND {field_id_left} > -1" query_left += aql self.fill_table_by_query(tbl_all_rows, query_left) self.hide_colums(tbl_all_rows, hide_left) tbl_all_rows.setColumnWidth(1, 200) # fill QTableView selected_rows tbl_selected_rows = dialog.findChild(QTableView, "selected_rows") tbl_selected_rows.setSelectionBehavior(QAbstractItemView.SelectRows) query_right = f"SELECT {tableleft}.{name}, cur_user, {tableleft}.{field_id_left}, {tableright}.{field_id_right}" query_right += f" FROM {schema_name}.{tableleft}" query_right += f" JOIN {schema_name}.{tableright} ON {tableleft}.{field_id_left} = {tableright}.{field_id_right}" query_right += " WHERE cur_user = current_user" self.fill_table_by_query(tbl_selected_rows, query_right) self.hide_colums(tbl_selected_rows, hide_right) tbl_selected_rows.setColumnWidth(0, 200) # Button select dialog.btn_select.clicked.connect( partial(self.multi_rows_selector, tbl_all_rows, tbl_selected_rows, field_id_left, tableright, field_id_right, query_left, query_right, field_id_right)) # Button unselect query_delete = f"DELETE FROM {schema_name}.{tableright}" query_delete += f" WHERE current_user = cur_user AND {tableright}.{field_id_right}=" dialog.btn_unselect.clicked.connect( partial(self.unselector, tbl_all_rows, tbl_selected_rows, query_delete, query_left, query_right, field_id_right)) # QLineEdit dialog.txt_name.textChanged.connect( partial(self.query_like_widget_text, dialog, dialog.txt_name, tbl_all_rows, tableleft, tableright, field_id_right, field_id_left, name)) # Order control tbl_all_rows.horizontalHeader().sectionClicked.connect( partial(self.order_by_column, tbl_all_rows, query_left)) tbl_selected_rows.horizontalHeader().sectionClicked.connect( partial(self.order_by_column, tbl_selected_rows, query_right)) def order_by_column(self, qtable, query, idx): """ :param qtable: QTableView widget :param query: Query for populate QsqlQueryModel :param idx: The index of the clicked column :return: """ oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable.horizontalHeader().sortIndicatorOrder() col_to_sort = qtable.model().headerData(idx, Qt.Horizontal) query += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable, query) self.refresh_map_canvas() def hide_colums(self, widget, comuns_to_hide): for i in range(0, len(comuns_to_hide)): widget.hideColumn(comuns_to_hide[i]) def unselector(self, qtable_left, qtable_right, query_delete, query_left, query_right, field_id_right): selected_list = qtable_right.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return expl_id = [] for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = str(qtable_right.model().record(row).value(field_id_right)) expl_id.append(id_) for i in range(0, len(expl_id)): self.controller.execute_sql(query_delete + str(expl_id[i])) # Refresh oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable_left.horizontalHeader().sortIndicatorOrder() idx = qtable_left.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_left.model().headerData(idx, Qt.Horizontal) query_left += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_left, query_left) sort_order = qtable_right.horizontalHeader().sortIndicatorOrder() idx = qtable_right.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_right.model().headerData(idx, Qt.Horizontal) query_right += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_right, query_right) self.refresh_map_canvas() def multi_rows_selector(self, qtable_left, qtable_right, id_ori, tablename_des, id_des, query_left, query_right, field_id): """ :param qtable_left: QTableView origin :param qtable_right: QTableView destini :param id_ori: Refers to the id of the source table :param tablename_des: table destini :param id_des: Refers to the id of the target table, on which the query will be made :param query_right: :param query_left: :param field_id: """ selected_list = qtable_left.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return expl_id = [] curuser_list = [] for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = qtable_left.model().record(row).value(id_ori) expl_id.append(id_) curuser = qtable_left.model().record(row).value("cur_user") curuser_list.append(curuser) for i in range(0, len(expl_id)): # Check if expl_id already exists in expl_selector sql = ( f"SELECT DISTINCT({id_des}, cur_user)" f" FROM {tablename_des}" f" WHERE {id_des} = '{expl_id[i]}' AND cur_user = current_user" ) row = self.controller.get_row(sql) if row: # if exist - show warning message = "Id already selected" self.controller.show_info_box(message, "Info", parameter=str(expl_id[i])) else: sql = (f"INSERT INTO {tablename_des} ({field_id}, cur_user) " f" VALUES ({expl_id[i]}, current_user)") self.controller.execute_sql(sql) # Refresh oder_by = {0: "ASC", 1: "DESC"} sort_order = qtable_left.horizontalHeader().sortIndicatorOrder() idx = qtable_left.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_left.model().headerData(idx, Qt.Horizontal) query_left += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_right, query_right) sort_order = qtable_right.horizontalHeader().sortIndicatorOrder() idx = qtable_right.horizontalHeader().sortIndicatorSection() col_to_sort = qtable_right.model().headerData(idx, Qt.Horizontal) query_right += f" ORDER BY {col_to_sort} {oder_by[sort_order]}" self.fill_table_by_query(qtable_left, query_left) self.refresh_map_canvas() def fill_table_psector(self, widget, table_name, set_edit_strategy=QSqlTableModel.OnManualSubmit): """ Set a model with selected @table_name. Attach that model to selected table """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model self.model = QSqlTableModel() self.model.setTable(table_name) self.model.setEditStrategy(set_edit_strategy) self.model.setSort(0, 0) self.model.select() # Check for errors if self.model.lastError().isValid(): self.controller.show_warning(self.model.lastError().text()) # Attach model to table view widget.setModel(self.model) def fill_table(self, widget, table_name, set_edit_strategy=QSqlTableModel.OnManualSubmit, expr_filter=None): """ Set a model with selected filter. Attach that model to selected table """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model self.model = QSqlTableModel() self.model.setTable(table_name) self.model.setEditStrategy(set_edit_strategy) self.model.setSort(0, 0) self.model.select() # Check for errors if self.model.lastError().isValid(): self.controller.show_warning(self.model.lastError().text()) # Attach model to table view widget.setModel(self.model) if expr_filter: widget.model().setFilter(expr_filter) def fill_table_by_query(self, qtable, query): """ :param qtable: QTableView to show :param query: query to set model """ model = QSqlQueryModel() model.setQuery(query) qtable.setModel(model) qtable.show() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) def query_like_widget_text(self, dialog, text_line, qtable, tableleft, tableright, field_id_r, field_id_l, name='name'): """ Fill the QTableView by filtering through the QLineEdit""" query = utils_giswater.getWidgetText(dialog, text_line, return_string_null=False).lower() sql = ( f"SELECT * FROM {tableleft} WHERE {name} NOT IN " f"(SELECT {tableleft}.{name} FROM {tableleft}" f" RIGHT JOIN {tableright}" f" ON {tableleft}.{field_id_l} = {tableright}.{field_id_r}" f" WHERE cur_user = current_user) AND LOWER({name}::text) LIKE '%{query}%'" ) self.fill_table_by_query(qtable, sql) def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr_filter is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return False, expr return True, expr def refresh_map_canvas(self, restore_cursor=False): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() if restore_cursor: self.set_cursor_restore() def set_cursor_wait(self): """ Change cursor to 'WaitCursor' """ QApplication.setOverrideCursor(Qt.WaitCursor) def set_cursor_restore(self): """ Restore to previous cursors """ QApplication.restoreOverrideCursor() def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def set_table_columns(self, dialog, widget, table_name, sort_order=0, isQStandardItemModel=False): """ Configuration of tables. Set visibility and width of columns """ widget = utils_giswater.getWidget(dialog, widget) if not widget: return # Set width and alias of visible columns columns_to_delete = [] sql = (f"SELECT column_index, width, alias, status" f" FROM config_client_forms" f" WHERE table_id = '{table_name}'" f" ORDER BY column_index") rows = self.controller.get_rows(sql, commit=True, log_info=False) if not rows: return for row in rows: if not row['status']: columns_to_delete.append(row['column_index'] - 1) else: width = row['width'] if width is None: width = 100 widget.setColumnWidth(row['column_index'] - 1, width) widget.model().setHeaderData(row['column_index'] - 1, Qt.Horizontal, row['alias']) # Set order if isQStandardItemModel: widget.model().sort(sort_order, Qt.AscendingOrder) else: widget.model().setSort(sort_order, Qt.AscendingOrder) widget.model().select() # Delete columns for column in columns_to_delete: widget.hideColumn(column) return widget def connect_signal_selection_changed(self, option): """ Connect signal selectionChanged """ try: if option == "mincut_connec": self.canvas.selectionChanged.connect( partial(self.snapping_selection_connec)) elif option == "mincut_hydro": self.canvas.selectionChanged.connect( partial(self.snapping_selection_hydro)) except Exception: pass def disconnect_signal_selection_changed(self): """ Disconnect signal selectionChanged """ try: self.canvas.selectionChanged.disconnect() except Exception: pass finally: self.iface.actionPan().trigger() def set_label_current_psector(self, dialog): sql = ( "SELECT t1.name FROM plan_psector AS t1 " " INNER JOIN config_param_user AS t2 ON t1.psector_id::text = t2.value " " WHERE t2.parameter='psector_vdefault' AND cur_user = current_user" ) row = self.controller.get_row(sql) if not row: return utils_giswater.setWidgetText(dialog, 'lbl_vdefault_psector', row[0]) def multi_rows_delete(self, widget, table_name, column_id): """ Delete selected elements of the table :param QTableView widget: origin :param table_name: table origin :param column_id: Refers to the id of the source table """ # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return inf_text = "" list_id = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value(str(column_id)) inf_text += f"{id_}, " list_id += f"'{id_}', " inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: sql = f"DELETE FROM {table_name}" sql += f" WHERE {column_id} IN ({list_id})" self.controller.execute_sql(sql) widget.model().select() def select_features_by_expr(self, layer, expr): """ Select features of @layer applying @expr """ if not layer: return if expr is None: layer.removeSelection() else: it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result and select them id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) else: layer.removeSelection() def hide_void_groupbox(self, dialog): """ Hide empty grupbox """ grbox_list = dialog.findChildren(QGroupBox) for grbox in grbox_list: widget_list = grbox.findChildren(QWidget) if len(widget_list) == 0: grbox.setVisible(False) def zoom_to_selected_features(self, layer, geom_type=None, zoom=None): """ Zoom to selected features of the @layer with @geom_type """ if not layer: return self.iface.setActiveLayer(layer) self.iface.actionZoomToSelected().trigger() if geom_type: # Set scale = scale_zoom if geom_type in ('node', 'connec', 'gully'): scale = self.scale_zoom # Set scale = max(current_scale, scale_zoom) elif geom_type == 'arc': scale = self.iface.mapCanvas().scale() if int(scale) < int(self.scale_zoom): scale = self.scale_zoom else: scale = 5000 if zoom is not None: scale = zoom self.iface.mapCanvas().zoomScale(float(scale)) def set_completer(self, tablename, widget, field_search, color='black'): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ if not widget: return # Set SQL sql = (f"SELECT DISTINCT({field_search})" f" FROM {tablename}" f" ORDER BY {field_search}") row = self.controller.get_rows(sql, commit=True) for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(0) self.completer.popup().setStyleSheet("color: " + color + ";") widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(row) self.completer.setModel(model) def get_max_rectangle_from_coords(self, list_coord): """ Returns the minimum rectangle(x1, y1, x2, y2) of a series of coordinates :type list_coord: list of coors in format ['x1 y1', 'x2 y2',....,'x99 y99'] """ coords = list_coord.group(1) polygon = coords.split(',') x, y = polygon[0].split(' ') min_x = x # start with something much higher than expected min min_y = y max_x = x # start with something much lower than expected max max_y = y for i in range(0, len(polygon)): x, y = polygon[i].split(' ') if x < min_x: min_x = x if x > max_x: max_x = x if y < min_y: min_y = y if y > max_y: max_y = y return max_x, max_y, min_x, min_y def zoom_to_rectangle(self, x1, y1, x2, y2, margin=5): rect = QgsRectangle( float(x1) - margin, float(y1) - margin, float(x2) + margin, float(y2) + margin) self.canvas.setExtent(rect) self.canvas.refresh() def create_action(self, action_name, action_group, icon_num=None, text=None): """ Creates a new action with selected parameters """ icon = None icon_folder = self.plugin_dir + '/icons/' icon_path = icon_folder + icon_num + '.png' if os.path.exists(icon_path): icon = QIcon(icon_path) if icon is None: action = QAction(text, action_group) else: action = QAction(icon, text, action_group) action.setObjectName(action_name) return action def set_wait_cursor(self): QApplication.instance().setOverrideCursor(Qt.WaitCursor) def set_arrow_cursor(self): QApplication.instance().setOverrideCursor(Qt.ArrowCursor) def delete_layer_from_toc(self, layer_name): """ Delete layer from toc if exist """ layer = None for lyr in list(QgsProject.instance().mapLayers().values()): if lyr.name() == layer_name: layer = lyr break if layer is not None: QgsProject.instance().removeMapLayer(layer) self.delete_layer_from_toc(layer_name) def create_body(self, form='', feature='', filter_fields='', extras=None): """ Create and return parameters as body to functions""" client = '"client":{"device":9, "infoType":100, "lang":"ES"}, ' form = f'"form":{{{form}}}, ' feature = f'"feature":{{{feature}}}, ' filter_fields = f'"filterFields":{{{filter_fields}}}' page_info = '"pageInfo":{}' data = f'"data":{{{filter_fields}, {page_info}' if extras is not None: data += ', ' + extras data += '}' body = "" + client + form + feature + data return body def set_layers_visible(self, layers): for layer in layers: lyr = self.controller.get_layer_by_tablename(layer) if lyr: self.controller.set_layer_visible(lyr) def add_temp_layer(self, dialog, data, function_name, force_tab=True, reset_text=True, tab_idx=1, del_old_layers=True): if del_old_layers: self.delete_layer_from_toc(function_name) srid = self.controller.plugin_settings_value('srid') for k, v in list(data.items()): if str(k) == 'setVisibleLayers': self.set_layers_visible(v) elif str(k) == "info": self.populate_info_text(dialog, data, force_tab, reset_text, tab_idx) else: counter = len(data[k]['values']) if counter > 0: counter = len(data[k]['values']) geometry_type = data[k]['geometryType'] v_layer = QgsVectorLayer( f"{geometry_type}?crs=epsg:{srid}", function_name, 'memory') self.populate_vlayer(v_layer, data, k, counter) if 'qmlPath' in data[k]: qml_path = data[k]['qmlPath'] self.load_qml(v_layer, qml_path) def populate_info_text(self, dialog, data, force_tab=True, reset_text=True, tab_idx=1): change_tab = False text = utils_giswater.getWidgetText(dialog, dialog.txt_infolog, return_string_null=False) if reset_text: text = "" for item in data['info']['values']: if 'message' in item: if item['message'] is not None: text += str(item['message']) + "\n" if force_tab: change_tab = True else: text += "\n" utils_giswater.setWidgetText(dialog, 'txt_infolog', text + "\n") qtabwidget = dialog.findChild(QTabWidget, 'mainTab') if change_tab and qtabwidget is not None: qtabwidget.setCurrentIndex(tab_idx) return change_tab def populate_vlayer(self, virtual_layer, data, layer_type, counter): prov = virtual_layer.dataProvider() # Enter editing mode virtual_layer.startEditing() if counter > 0: for key, value in list(data[layer_type]['values'][0].items()): # add columns if str(key) != 'the_geom': prov.addAttributes([QgsField(str(key), QVariant.String)]) # Add features for item in data[layer_type]['values']: attributes = [] fet = QgsFeature() for k, v in list(item.items()): if str(k) != 'the_geom': attributes.append(v) if str(k) in 'the_geom': sql = f"SELECT St_AsText('{v}')" row = self.controller.get_row(sql, log_sql=False) geometry = QgsGeometry.fromWkt(str(row[0])) fet.setGeometry(geometry) fet.setAttributes(attributes) prov.addFeatures([fet]) # Commit changes virtual_layer.commitChanges() QgsProject.instance().addMapLayer(virtual_layer, False) root = QgsProject.instance().layerTreeRoot() my_group = root.findGroup('GW Functions results') if my_group is None: my_group = root.insertGroup(0, 'GW Functions results') my_group.insertLayer(0, virtual_layer) def get_composers_list(self): layour_manager = QgsProject.instance().layoutManager().layouts() active_composers = [layout for layout in layour_manager] return active_composers def get_composer_index(self, name): index = 0 composers = self.get_composers_list() for comp_view in composers: composer_name = comp_view.name() if composer_name == name: break index += 1 return index def get_all_actions(self): self.controller.log_info(str("TEST")) actions_list = self.iface.mainWindow().findChildren(QAction) for action in actions_list: self.controller.log_info(str(action.objectName())) action.triggered.connect(partial(self.show_action_name, action)) def show_action_name(self, action): self.controller.log_info(str(action.objectName())) def set_restriction(self, dialog, widget_to_ignore, restriction): """ Set all widget enabled(False) or readOnly(True) except those on the tuple :param dialog: :param widget_to_ignore: tuple = ('widgetname1', 'widgetname2', 'widgetname3', ...) :param restriction: roles that do not have access. tuple = ('role1', 'role1', 'role1', ...) :return: """ role = self.controller.get_restriction() if role in restriction: widget_list = dialog.findChildren(QWidget) for widget in widget_list: if widget.objectName() in widget_to_ignore: continue # Set editable/readonly if type(widget) in (QLineEdit, QDoubleSpinBox, QTextEdit): widget.setReadOnly(True) widget.setStyleSheet( "QWidget {background: rgb(242, 242, 242);color: rgb(100, 100, 100)}" ) elif type(widget) in (QComboBox, QCheckBox, QTableView, QPushButton): widget.setEnabled(False) def set_dates_from_to(self, widget_from, widget_to, table_name, field_from, field_to): sql = (f"SELECT MIN(LEAST({field_from}, {field_to}))," f" MAX(GREATEST({field_from}, {field_to}))" f" FROM {table_name}") row = self.controller.get_row(sql, log_sql=False) current_date = QDate.currentDate() if row: if row[0]: widget_from.setDate(row[0]) else: widget_from.setDate(current_date) if row[1]: widget_to.setDate(row[1]) else: widget_to.setDate(current_date) def get_values_from_catalog(self, table_name, typevalue, order_by='id'): sql = (f"SELECT id, idval" f" FROM {table_name}" f" WHERE typevalue = '{typevalue}'" f" ORDER BY {order_by}") rows = self.controller.get_rows(sql, commit=True) return rows def integer_validator(self, value, widget, btn_accept): """ Check if the value is an integer or not. This function is called in def set_datatype_validator(self, value, widget, btn) widget = getattr(self, f"{widget.property('datatype')}_validator")( value, widget, btn) """ if value is None or bool(re.search("^\d*$", value)): widget.setStyleSheet( "QLineEdit{background:rgb(255, 255, 255); color:rgb(0, 0, 0)}") btn_accept.setEnabled(True) else: widget.setStyleSheet("border: 1px solid red") btn_accept.setEnabled(False) def double_validator(self, value, widget, btn_accept): """ Check if the value is double or not. This function is called in def set_datatype_validator(self, value, widget, btn) widget = getattr(self, f"{widget.property('datatype')}_validator")( value, widget, btn) """ if value is None or bool(re.search("^\d*$", value)) or bool( re.search("^\d+\.\d+$", value)): widget.setStyleSheet( "QLineEdit{background:rgb(255, 255, 255); color:rgb(0, 0, 0)}") btn_accept.setEnabled(True) else: widget.setStyleSheet("border: 1px solid red") btn_accept.setEnabled(False) def load_qml(self, layer, qml_path): """ Apply QML style located in @qml_path in @layer """ if layer is None: return False if not os.path.exists(qml_path): self.controller.log_warning("File not found", parameter=qml_path) return False if not qml_path.endswith(".qml"): self.controller.log_warning("File extension not valid", parameter=qml_path) return False layer.loadNamedStyle(qml_path) layer.triggerRepaint() return True def open_file_path(self, filter_="All (*.*)"): """ Open QFileDialog """ msg = self.controller.tr("Select DXF file") path, filter_ = QFileDialog.getOpenFileName(None, msg, "", filter_) return path, filter_ def show_exceptions_msg(self, title, msg=""): cat_exception = { 'KeyError': 'Key on returned json from ddbb is missed.' } self.dlg_info = BasicInfo() self.dlg_info.btn_accept.setVisible(False) self.dlg_info.btn_close.clicked.connect( partial(self.close_dialog, self.dlg_info)) self.dlg_info.setWindowTitle(title) utils_giswater.setWidgetText(self.dlg_info, self.dlg_info.txt_info, msg) self.open_dialog(self.dlg_info)
class TmParentAction(object): def __init__(self, iface, settings, controller, plugin_dir): """ Class constructor """ # Initialize instance attributes self.tree_manage_version = "1.0" self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.dao = self.controller.dao self.schema_name = self.controller.schema_name self.project_type = None self.file_gsw = None self.gsw_settings = None self.lazy_widget = None def set_controller(self, controller): """ Set controller class """ self.controller = controller self.schema_name = self.controller.schema_name def get_plugin_version(self): """ Get plugin version from metadata.txt file """ # Check if metadata file exists metadata_file = os.path.join(self.plugin_dir, 'metadata.txt') if not os.path.exists(metadata_file): message = "Metadata file not found" + metadata_file self.controller.show_warning(message, parameter=metadata_file) return None metadata = configparser.ConfigParser() metadata.read(metadata_file) plugin_version = metadata.get('general', 'version') if plugin_version is None: message = "Plugin version not found" self.controller.show_warning(message) return plugin_version def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: screens = ctypes.windll.user32 screen_x = screens.GetSystemMetrics(78) screen_y = screens.GetSystemMetrics(79) x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") width = self.controller.plugin_settings_value( dialog.objectName() + "_width", dialog.property('width')) height = self.controller.plugin_settings_value( dialog.objectName() + "_height", dialog.property('height')) if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: if int(x) > screen_x: x = int(screen_x) - int(width) if int(y) > screen_y: y = int(screen_y) dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg self.controller.plugin_settings_set_value( dialog.objectName() + "_width", dialog.width()) self.controller.plugin_settings_set_value( dialog.objectName() + "_height", dialog.height()) self.controller.plugin_settings_set_value(dialog.objectName() + "_x", dialog.pos().x() + 8) self.controller.plugin_settings_set_value(dialog.objectName() + "_y", dialog.pos().y() + 31) def open_dialog(self, dlg=None, dlg_name=None, info=True, maximize_button=True, stay_on_top=True): """ Open dialog """ # Check database connection before opening dialog if not self.controller.check_db_connection(): return if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top, maximize/minimize button and information button # if info is True maximize flag will be ignored # To enable maximize button you must set info to False flags = Qt.WindowCloseButtonHint if info: flags |= Qt.WindowSystemMenuHint | Qt.WindowContextHelpButtonHint else: if maximize_button: flags |= Qt.WindowMinMaxButtonsHint if stay_on_top: flags |= Qt.WindowStaysOnTopHint dlg.setWindowFlags(flags) # Open dialog if issubclass(type(dlg), GwDialog): dlg.open() elif issubclass(type(dlg), GwMainWindow): dlg.show() else: dlg.show() def close_dialog(self, dlg=None): """ Close dialog """ try: self.save_settings(dlg) dlg.close() map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.iface.actionPan().trigger() except AttributeError: pass def hide_colums(self, widget, comuns_to_hide): for i in range(0, len(comuns_to_hide)): widget.hideColumn(comuns_to_hide[i]) def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr_filter is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return False, expr return True, expr def set_table_columns(self, dialog, widget, table_name, project_type=None): """ Configuration of tables. Set visibility and width of columns """ widget = utils_giswater.getWidget(dialog, widget) if not widget: return # Set width and alias of visible columns columns_to_delete = [] sql = (f"SELECT columnindex, width, alias, status" f" FROM config_form_tableview" f" WHERE tablename = '{table_name}'") if project_type is not None: sql += f" AND project_type = '{project_type}' " sql += " ORDER BY column_index" rows = self.controller.get_rows(sql, log_info=False) if not rows: return for row in rows: if not row['status']: columns_to_delete.append(row['columnindex'] - 1) else: width = row['width'] if width is not None: widget.setColumnWidth(row['columnindex'] - 1, width) widget.model().setHeaderData(row['columnindex'] - 1, Qt.Horizontal, row['alias']) widget.model().select() # Delete columns for column in columns_to_delete: widget.hideColumn(column) def set_completer_object(self, tablename, widget, field_search): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ if not widget: return # Set SQL sql = (f"SELECT DISTINCT({field_search})" f" FROM {tablename}" f" ORDER BY {field_search}") rows = self.controller.get_rows(sql) if rows is None: return for i in range(0, len(rows)): aux = rows[i] rows[i] = aux[0] # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(0) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(rows) self.completer.setModel(model) def refresh_map_canvas(self, restore_cursor=False): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() if restore_cursor: self.set_cursor_restore() def set_cursor_restore(self): """ Restore to previous cursors """ QApplication.restoreOverrideCursor() def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def fill_table(self, qtable, table_name, set_edit_triggers=QTableView.NoEditTriggers, expr_filter=None): """ Fill table @widget filtering query by @workcat_id Set a model with selected filter. Attach that model to selected table @setEditStrategy: 0: OnFieldChange 1: OnRowChange 2: OnManualSubmit """ expr = None if expr_filter: # Check expression (is_valid, expr) = self.check_expression(expr_filter) # @UnusedVariable if not is_valid: return expr # Set a model with selected filter expression if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.setSort(0, 0) model.select() # When change some field we need to refresh Qtableview and filter by psector_id qtable.setEditTriggers(set_edit_triggers) # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view if expr: qtable.setModel(model) qtable.model().setFilter(expr_filter) else: qtable.setModel(model) return expr def get_feature_by_id(self, layer, id, field_id): features = layer.getFeatures() for feature in features: if feature[field_id] == id: return feature return False def put_combobox(self, qtable, rows, combo_values, field, combo_pos, col_update): """ Set one column of a QtableView as QComboBox with values from database. :param qtable: QTableView to fill :param rows: List of items to set QComboBox (["..", "..."]) :param combo_values: List of items to populate QComboBox (["..", "..."]) :param field: Field to set QComboBox (String) :param combo_pos: Position of the column where we want to put the QComboBox (integer) :param col_update: Column to update into QTableView.Model() (integer) :return: """ self.controller.log_info(str()) for x, row in enumerate(rows): combo = QComboBox() combo.setSizeAdjustPolicy(2) # Populate QComboBox utils_giswater.set_item_data(combo, combo_values, 1) # Set QCombobox to wanted item utils_giswater.set_combo_itemData(combo, str(row[field]), 0) # Get index and put QComboBox into QTableView at index position idx = qtable.model().index(x, combo_pos) qtable.setIndexWidget(idx, combo) combo.currentIndexChanged.connect( partial(self.update_real_field, qtable, combo, x, combo_pos, col_update)) def update_real_field(self, qtable, combo, pos_x, combo_pos, col_update): """ Update values from QComboBox to QTableView :param qtable: QTableView Where update values :param combo: QComboBox from which we will take the value :param pos_x: Position of the row where we want to update value (integer) :param combo_pos: Position of the column where we want to put the QComboBox (integer) :param col_update: Column to update into QTableView.Model() (integer) :return: """ elem = combo.itemData(combo.currentIndex()) i = qtable.model().index(pos_x, combo_pos) qtable.model().setData(i, elem[0]) i = qtable.model().index(pos_x, col_update) qtable.model().setData(i, elem[0]) def create_body(self, form='', feature='', filter_fields='', extras=None): """ Create and return parameters as body to functions""" client = f'$${{"client":{{"device":4, "infoType":1, "lang":"ES"}}, ' form = '"form":{' + form + '}, ' feature = '"feature":{' + feature + '}, ' filter_fields = '"filterFields":{' + filter_fields + '}' page_info = '"pageInfo":{}' data = '"data":{' + filter_fields + ', ' + page_info if extras is not None: data += ', ' + extras data += f'}}}}$$' body = "" + client + form + feature + data return body def set_dates_from_to(self, widget_from, widget_to, table_name, field_from, field_to): sql = ("SELECT MIN(LEAST(" + field_from + ", " + field_to + "))," " MAX(GREATEST(" + field_from + ", " + field_to + "))" " FROM " + table_name + "") row = self.controller.get_row(sql, log_sql=False) current_date = QDate.currentDate() if row: if row[0]: widget_from.setDate(row[0]) else: widget_from.setDate(current_date) if row[1]: widget_to.setDate(row[1]) else: widget_to.setDate(current_date) def remove_selection(self): """ Remove selected features of all layers """ for layer in self.canvas.layers(): if type(layer) is QgsVectorLayer: layer.removeSelection() self.canvas.refresh() for a in self.iface.attributesToolBar().actions(): if a.objectName() == 'mActionDeselectAll': a.trigger() break