class ButtonLineEdit(QLineEdit): buttonClicked = pyqtSignal() def __init__(self, parent=None): super(ButtonLineEdit, self).__init__(parent) self.setPlaceholderText("Search") self.btnSearch = QToolButton(self) self.btnSearch.setIcon(QIcon(os.path.join(pluginPath, "icons", "search.svg"))) self.btnSearch.setStyleSheet("QToolButton { padding: 0px; }") self.btnSearch.setCursor(Qt.ArrowCursor) self.btnSearch.clicked.connect(self.buttonClicked.emit) frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) buttonSize = self.btnSearch.sizeHint() self.setStyleSheet("QLineEdit {{padding-right: {}px; }}".format(buttonSize.width() + frameWidth + 1)) self.setMinimumSize(max(self.minimumSizeHint().width(), buttonSize.width() + frameWidth * 2 + 2), max(self.minimumSizeHint().height(), buttonSize.height() + frameWidth * 2 + 2)) def resizeEvent(self, event): buttonSize = self.btnSearch.sizeHint() frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.btnSearch.move(self.rect().right() - frameWidth - buttonSize.width(), (self.rect().bottom() - buttonSize.height() + 1) / 2) super(ButtonLineEdit, self).resizeEvent(event)
def append_menu_buttons(self): """ Append menus and buttons to appropriate toolbar :return: """ # add to QGIS menu if PluginSettings.move_to_layers_menu(): self.iface.addLayerMenu().addMenu(self.menu) else: # need workaround for WebMenu _temp_act = QAction('temp', self.iface.mainWindow()) self.iface.addPluginToWebMenu("_tmp", _temp_act) self.iface.webMenu().addMenu(self.menu) self.iface.removePluginWebMenu("_tmp", _temp_act) # add to QGIS toolbar toolbutton = QToolButton() toolbutton.setPopupMode(QToolButton.InstantPopup) toolbutton.setMenu(self.menu) toolbutton.setIcon(self.menu.icon()) toolbutton.setText(self.menu.title()) toolbutton.setToolTip(self.menu.title()) # self.tb_action = toolbutton.defaultAction() # print "self.tb_action: ", self.tb_action if PluginSettings.move_to_layers_menu(): self.tb_action = self.iface.layerToolBar().addWidget(toolbutton) self.iface.layerToolBar().addAction(self.qms_search_action) else: self.tb_action = self.iface.webToolBar().addWidget(toolbutton) self.iface.webToolBar().addAction(self.qms_search_action)
def addProviderActions(self, provider): if provider.id() in ProviderActions.actions: toolbarButton = QToolButton() toolbarButton.setObjectName('provideraction_' + provider.id()) toolbarButton.setIcon(provider.icon()) toolbarButton.setToolTip(provider.name()) toolbarButton.setPopupMode(QToolButton.InstantPopup) actions = ProviderActions.actions[provider.id()] menu = QMenu(provider.name(), self) for action in actions: action.setData(self) act = QAction(action.name, menu) act.triggered.connect(action.execute) menu.addAction(act) toolbarButton.setMenu(menu) self.processingToolbar.addWidget(toolbarButton)
def createWidget(self): if self.dialogType == DIALOG_MODELER: self.combo = QComboBox() widget = QWidget() layout = QHBoxLayout() layout.setMargin(0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(1) layout.addWidget(self.combo) btn = QToolButton() btn.setIcon(QgsApplication.getThemeIcon("mActionSetProjection.svg")) btn.setToolTip(self.tr("Select CRS")) btn.clicked.connect(self.selectProjection) layout.addWidget(btn) widget.setLayout(layout) self.combo.setEditable(True) crss = self.dialog.getAvailableValuesOfType(ParameterCrs) for crs in crss: self.combo.addItem(self.dialog.resolveValueDescription(crs), crs) raster = self.dialog.getAvailableValuesOfType(ParameterRaster, OutputRaster) vector = self.dialog.getAvailableValuesOfType(ParameterVector, OutputVector) for r in raster: self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(r), r) for v in vector: self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(v), v) if not self.param.default: self.combo.setEditText(self.param.default) return widget else: widget = QgsProjectionSelectionWidget() if self.param.optional: widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True) if self.param.default: if self.param.default == 'ProjectCrs': crs = QgsProject.instance().crs() else: crs = QgsCoordinateReferenceSystem(self.param.default) widget.setCrs(crs) else: widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True) return widget
class PasswordLineEdit(QLineEdit): def __init__(self, parent=None): QLineEdit.__init__(self, parent) self.setPlaceholderText(self.tr("Password")) self.setEchoMode(QLineEdit.Password) self.btnIcon = QToolButton(self) self.btnIcon.setIcon(QIcon(os.path.join(iconsPath, "lock.svg"))) self.btnIcon.setEnabled(False) self.btnIcon.setStyleSheet( "QToolButton { border: none; padding: 0px; }") self.btnToggle = QToolButton(self) self.btnToggle.setIcon(QIcon(os.path.join(iconsPath, "eye-slash.svg"))) self.btnToggle.setCheckable(True) self.btnToggle.setToolTip(self.tr("Toggle password visibility")) self.btnToggle.setCursor(Qt.ArrowCursor) self.btnToggle.setStyleSheet( "QToolButton { border: none; padding: 0px; }") self.btnToggle.toggled.connect(self.togglePassword) frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) self.setStyleSheet( "QLineEdit {{ padding-right: {}px; padding-left: {}px }} ".format( self.btnToggle.sizeHint().width() + frameWidth + 1, self.btnIcon.sizeHint().width() + frameWidth + 1)) msz = self.minimumSizeHint() self.setMinimumSize( max(msz.width(), self.btnToggle.sizeHint().height() + frameWidth * 2 + 2), max(msz.height(), self.btnToggle.sizeHint().height() + frameWidth * 2 + 2)) def resizeEvent(self, event): frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) sz = self.btnIcon.sizeHint() self.btnIcon.move(frameWidth + 1, (self.rect().bottom() + 1 - sz.height()) / 2) sz = self.btnToggle.sizeHint() self.btnToggle.move(self.rect().right() - frameWidth - sz.width(), (self.rect().bottom() + 1 - sz.height()) / 2) def togglePassword(self, toggled): if toggled: self.setEchoMode(QLineEdit.Normal) self.btnToggle.setIcon(QIcon(os.path.join(iconsPath, "eye.svg"))) else: self.setEchoMode(QLineEdit.Password) self.btnToggle.setIcon( QIcon(os.path.join(iconsPath, "eye-slash.svg")))
def createWidget(self): if self.dialogType == DIALOG_MODELER: self.combo = QComboBox() widget = QWidget() layout = QHBoxLayout() layout.setMargin(0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(1) layout.addWidget(self.combo) btn = QToolButton() btn.setIcon(QgsApplication.getThemeIcon("mActionSetProjection.svg")) btn.setToolTip(self.tr("Select CRS")) btn.clicked.connect(self.selectProjection) layout.addWidget(btn) widget.setLayout(layout) self.combo.setEditable(True) crss = self.dialog.getAvailableValuesOfType(ParameterCrs) for crs in crss: self.combo.addItem(self.dialog.resolveValueDescription(crs), crs) raster = self.dialog.getAvailableValuesOfType(ParameterRaster, OutputRaster) vector = self.dialog.getAvailableValuesOfType(ParameterVector, OutputVector) for r in raster: self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(r), r) for v in vector: self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(v), v) if not self.param.default: self.combo.setEditText(self.param.default) return widget else: widget = QgsProjectionSelectionWidget() if self.param.optional: widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True) if self.param.default: crs = QgsCoordinateReferenceSystem(self.param.default) widget.setCrs(crs) return widget
class Ui_PostNAS_SearchDialogBase(object): def setupUi(self, PostNAS_SearchDialogBase): PostNAS_SearchDialogBase.setObjectName(_fromUtf8("PostNAS_SearchDialogBase")) PostNAS_SearchDialogBase.resize(501, 337) self.gridLayout = QGridLayout(PostNAS_SearchDialogBase) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.treeWidget = QTreeWidget(PostNAS_SearchDialogBase) self.treeWidget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeWidget.setHeaderHidden(True) self.treeWidget.setObjectName(_fromUtf8("treeWidget")) self.treeWidget.headerItem().setText(0, _fromUtf8("1")) self.gridLayout.addWidget(self.treeWidget, 1, 0, 1, 3) self.lineEdit = QLineEdit(PostNAS_SearchDialogBase) self.lineEdit.setObjectName(_fromUtf8("lineEdit")) self.gridLayout.addWidget(self.lineEdit, 0, 0, 1, 3) self.showButton = QToolButton(PostNAS_SearchDialogBase) self.showButton.setEnabled(False) icon = QtGui.QIcon() icon.addPixmap(QPixmap(_fromUtf8(":/plugins/PostNAS_Search/search_16x16.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.showButton.setIcon(icon) self.showButton.setObjectName(_fromUtf8("showButton")) self.gridLayout.addWidget(self.showButton, 2, 2, 1, 1) self.resetButton = QToolButton(PostNAS_SearchDialogBase) self.resetButton.setEnabled(False) icon1 = QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/plugins/PostNAS_Search/marker-delete.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.resetButton.setIcon(icon1) self.resetButton.setObjectName(_fromUtf8("resetButton")) self.gridLayout.addWidget(self.resetButton, 2, 1, 1, 1) self.retranslateUi(PostNAS_SearchDialogBase) QtCore.QMetaObject.connectSlotsByName(PostNAS_SearchDialogBase) def retranslateUi(self, PostNAS_SearchDialogBase): PostNAS_SearchDialogBase.setWindowTitle(_translate("PostNAS_SearchDialogBase", "KU_Search", None)) self.showButton.setToolTip(_translate("PostNAS_SearchDialogBase", "Auswahl anzeigen", None)) self.showButton.setText(_translate("PostNAS_SearchDialogBase", "Anzeigen", None)) self.resetButton.setToolTip(_translate("PostNAS_SearchDialogBase", "Ergebnis löschen", None)) self.resetButton.setText(_translate("PostNAS_SearchDialogBase", "Reset", None))
def _setup_str_tab(self, is_party_unit: bool): """ Creates the STR relationship tab """ from stdm.ui.feature_details import DetailsTreeView layout = QVBoxLayout() hl = QHBoxLayout() add_btn = QToolButton(self) add_btn.setText(self.tr('Create STR')) add_btn.setIcon(GuiUtils.get_icon('add.png')) hl.addWidget(add_btn) add_btn.clicked.connect(self._create_str) edit_btn = QToolButton(self) edit_btn.setText(self.tr('Edit')) edit_btn.setIcon(GuiUtils.get_icon('edit.png')) edit_btn.setDisabled(True) hl.addWidget(edit_btn) view_document_btn = QToolButton(self) view_document_btn.setText(self.tr('View Supporting Documents')) view_document_btn.setIcon(GuiUtils.get_icon('document.png')) view_document_btn.setDisabled(True) hl.addWidget(view_document_btn) hl.addStretch() layout.addLayout(hl) self.details_tree_view = DetailsTreeView( parent=self, plugin=self.plugin, edit_button=edit_btn, view_document_button=view_document_btn) self.details_tree_view.activate_feature_details( True, follow_layer_selection=False) self.details_tree_view.model.clear() if is_party_unit: self.details_tree_view.search_party(self.entity, [self.ent_model.id]) else: self.details_tree_view.search_spatial_unit(self.entity, [self.ent_model.id]) layout.addWidget(self.details_tree_view) w = QWidget() w.setLayout(layout) self.entity_tab_widget.addTab(w, self.tr('STR'))
class GwManageLayers: def __init__(self, iface, settings, controller, plugin_dir): """ Class to manage layers. Refactor code from giswater.py """ self.iface = iface self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.available_layers = None self.add_layer = GwLayerTools(self.iface, self.settings, self.controller, self.plugin_dir) self.project_type = controller.get_project_type() self.schema_name = controller.schema_name self.project_vars = global_vars.qgis_tools.get_qgis_project_variables() self.qgis_project_infotype = self.project_vars['infotype'] self.qgis_project_add_schema = self.project_vars['add_schema'] def config_layers(self): status = self.manage_layers() if not status: return False # Set config layer fields when user add new layer into the TOC # QgsProject.instance().legendLayersAdded.connect(self.get_new_layers_name) # Put add layers button into toc # self.add_layers_button() # Set project layers with gw_fct_getinfofromid: This process takes time for user # Set background task 'ConfigLayerFields' description = f"ConfigLayerFields" task_get_layers = GwConfigLayerTask(description, self.controller) task_get_layers.set_params(self.project_type, self.schema_name, self.qgis_project_infotype) QgsApplication.taskManager().addTask(task_get_layers) QgsApplication.taskManager().triggerTask(task_get_layers) return True def manage_layers(self): """ Get references to project main layers """ # Check if we have any layer loaded layers = self.controller.get_layers() if len(layers) == 0: return False if self.project_type in ('ws', 'ud'): QApplication.setOverrideCursor(Qt.ArrowCursor) self.check_project_result = GwProjectCheck(self.iface, self.settings, self.controller, self.plugin_dir) self.check_project_result.set_controller(self.controller) # check project status, result = self.check_project_result.populate_audit_check_project( layers, "true") try: if 'actions' in result['body']: if 'useGuideMap' in result['body']['actions']: guided_map = result['body']['actions']['useGuideMap'] if guided_map: self.controller.log_info("manage_guided_map") self.manage_guided_map() except Exception as e: self.controller.log_info(str(e)) finally: QApplication.restoreOverrideCursor() return status return True def get_new_layers_name(self, layers_list): layers_name = [] for layer in layers_list: layer_source = self.controller.get_layer_source(layer) # Collect only the layers of the work scheme if 'schema' in layer_source: schema = layer_source['schema'] if schema and schema.replace('"', '') == self.schema_name: layers_name.append(layer.name()) self.set_layer_config(layers_name) def set_layer_config(self, layers): """ Set layer fields configured according to client configuration. At the moment manage: Column names as alias, combos as ValueMap, typeahead as textedit""" self.controller.log_info("Start set_layer_config") msg_failed = "" msg_key = "" for layer_name in layers: layer = self.controller.get_layer_by_tablename(layer_name) if not layer: continue feature = '"tableName":"' + str( layer_name) + '", "id":"", "isLayer":true' extras = f'"infoType":"{self.qgis_project_infotype}"' body = self.create_body(feature=feature, extras=extras) complet_result = self.controller.get_json('gw_fct_getinfofromid', body) if not complet_result: continue for field in complet_result['body']['data']['fields']: valuemap_values = {} # Get column index fieldIndex = layer.fields().indexFromName(field['columnname']) # Hide selected fields according table config_api_form_fields.hidden if 'hidden' in field: self.set_column_visibility(layer, field['columnname'], field['hidden']) # Set alias column if field['label']: layer.setFieldAlias(fieldIndex, field['label']) if 'widgetcontrols' in field: # Set multiline fields according table config_api_form_fields.widgetcontrols['setQgisMultiline'] if field[ 'widgetcontrols'] is not None and 'setQgisMultiline' in field[ 'widgetcontrols']: self.set_column_multiline(layer, field, fieldIndex) # Set field constraints if field[ 'widgetcontrols'] and 'setQgisConstraints' in field[ 'widgetcontrols']: if field['widgetcontrols'][ 'setQgisConstraints'] is True: layer.setFieldConstraint( fieldIndex, QgsFieldConstraints.ConstraintNotNull, QgsFieldConstraints.ConstraintStrengthSoft) layer.setFieldConstraint( fieldIndex, QgsFieldConstraints.ConstraintUnique, QgsFieldConstraints.ConstraintStrengthHard) if 'ismandatory' in field and not field['ismandatory']: layer.setFieldConstraint( fieldIndex, QgsFieldConstraints.ConstraintNotNull, QgsFieldConstraints.ConstraintStrengthSoft) # Manage editability self.set_read_only(layer, field, fieldIndex) # delete old values on ValueMap editor_widget_setup = QgsEditorWidgetSetup( 'ValueMap', {'map': valuemap_values}) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) # Manage new values in ValueMap if field['widgettype'] == 'combo': if 'comboIds' in field: # Set values for i in range(0, len(field['comboIds'])): valuemap_values[field['comboNames'] [i]] = field['comboIds'][i] # Set values into valueMap editor_widget_setup = QgsEditorWidgetSetup( 'ValueMap', {'map': valuemap_values}) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) elif field['widgettype'] == 'check': config = { 'CheckedState': 'true', 'UncheckedState': 'false' } editor_widget_setup = QgsEditorWidgetSetup( 'CheckBox', config) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) elif field['widgettype'] == 'datetime': config = { 'allow_null': True, 'calendar_popup': True, 'display_format': 'yyyy-MM-dd', 'field_format': 'yyyy-MM-dd', 'field_iso_format': False } editor_widget_setup = QgsEditorWidgetSetup( 'DateTime', config) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) else: editor_widget_setup = QgsEditorWidgetSetup( 'TextEdit', {'IsMultiline': 'True'}) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) if msg_failed != "": self.controller.show_exceptions_msg("Execute failed.", msg_failed) if msg_key != "": self.controller.show_exceptions_msg( "Key on returned json from ddbb is missed.", msg_key) self.controller.log_info("Finish set_layer_config") def set_form_suppress(self, layers_list): """ Set form suppress on "Hide form on add feature (global settings) """ for layer_name in layers_list: layer = self.controller.get_layer_by_tablename(layer_name) if layer is None: continue config = layer.editFormConfig() config.setSuppress(0) layer.setEditFormConfig(config) def set_read_only(self, layer, field, field_index): """ Set field readOnly according to client configuration into config_api_form_fields (field 'iseditable') """ # Get layer config config = layer.editFormConfig() try: # Set field editability config.setReadOnly(field_index, not field['iseditable']) except KeyError: pass finally: # Set layer config layer.setEditFormConfig(config) def set_column_visibility(self, layer, col_name, hidden): """ Hide selected fields according table config_api_form_fields.hidden """ config = layer.attributeTableConfig() columns = config.columns() for column in columns: if column.name == str(col_name): column.hidden = hidden break config.setColumns(columns) layer.setAttributeTableConfig(config) def set_column_multiline(self, layer, field, fieldIndex): """ Set multiline selected fields according table config_api_form_fields.widgetcontrols['setQgisMultiline'] """ if field['widgettype'] == 'text': if field['widgetcontrols'] and 'setQgisMultiline' in field[ 'widgetcontrols']: editor_widget_setup = QgsEditorWidgetSetup( 'TextEdit', { 'IsMultiline': field['widgetcontrols']['setQgisMultiline'] }) layer.setEditorWidgetSetup(fieldIndex, editor_widget_setup) 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 manage_guided_map(self): """ Guide map works using ext_municipality """ self.layer_muni = self.controller.get_layer_by_tablename( 'ext_municipality') if self.layer_muni is None: return self.iface.setActiveLayer(self.layer_muni) self.controller.set_layer_visible(self.layer_muni) self.layer_muni.selectAll() self.iface.actionZoomToSelected().trigger() self.layer_muni.removeSelection() self.iface.actionSelect().trigger() self.iface.mapCanvas().selectionChanged.connect(self.selection_changed) cursor = self.get_cursor_multiple_selection() if cursor: self.iface.mapCanvas().setCursor(cursor) def selection_changed(self): """ Get selected muni_id and execute function setselectors """ muni_id = None features = self.layer_muni.getSelectedFeatures() for feature in features: muni_id = feature["muni_id"] self.controller.log_info(f"Selected muni_id: {muni_id}") break self.iface.mapCanvas().selectionChanged.disconnect() self.iface.actionZoomToSelected().trigger() self.layer_muni.removeSelection() if muni_id is None: return extras = f'"selectorType":"explfrommuni", "id":{muni_id}, "value":true, "isAlone":true, ' extras += f'"addSchema":"{self.qgis_project_add_schema}"' body = self.create_body(extras=extras) sql = f"SELECT gw_fct_setselectors({body})::text" row = self.controller.get_row(sql, commit=True) if row: self.iface.mapCanvas().refreshAllLayers() self.layer_muni.triggerRepaint() self.iface.actionPan().trigger() self.iface.actionZoomIn().trigger() def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ icon_path = self.plugin_dir + '/icons/211.png' if os.path.exists(icon_path): cursor = QCursor(QPixmap(icon_path)) else: cursor = None return cursor def add_layers_button(self): icon_path = self.plugin_dir + '/icons/306.png' dockwidget = self.iface.mainWindow().findChild(QDockWidget, 'Layers') toolbar = dockwidget.findChildren(QToolBar)[0] btn_exist = toolbar.findChild(QToolButton, 'gw_add_layers') if btn_exist is None: self.btn_add_layers = QToolButton() self.btn_add_layers.setIcon(QIcon(icon_path)) self.btn_add_layers.setObjectName('gw_add_layers') self.btn_add_layers.setToolTip('Load giswater layer') toolbar.addWidget(self.btn_add_layers) self.btn_add_layers.clicked.connect( partial(self.create_add_layer_menu)) def create_add_layer_menu(self): # Create main menu and get cursor click position main_menu = QMenu() cursor = QCursor() x = cursor.pos().x() y = cursor.pos().y() click_point = QPoint(x + 5, y + 5) schema_name = self.schema_name.replace('"', '') # Get parent layers sql = ( "SELECT distinct ( CASE parent_layer WHEN 'v_edit_node' THEN 'Node' " "WHEN 'v_edit_arc' THEN 'Arc' WHEN 'v_edit_connec' THEN 'Connec' " "WHEN 'v_edit_gully' THEN 'Gully' END ), parent_layer FROM cat_feature " "ORDER BY parent_layer") parent_layers = self.controller.get_rows(sql) for parent_layer in parent_layers: # Get child layers sql = ( f"SELECT DISTINCT(child_layer), lower(feature_type), cat_feature.id as alias, style as style_id, " f" group_layer " f" FROM cat_feature " f" LEFT JOIN config_table ON config_table.id = child_layer " f"WHERE parent_layer = '{parent_layer[1]}' " f"AND child_layer IN (" f" SELECT table_name FROM information_schema.tables" f" WHERE table_schema = '{schema_name}')" f" ORDER BY child_layer") child_layers = self.controller.get_rows(sql) if not child_layers: continue # Create sub menu sub_menu = main_menu.addMenu(str(parent_layer[0])) child_layers.insert( 0, ['Load all', 'Load all', 'Load all', 'Load all', 'Load all']) for child_layer in child_layers: # Create actions action = QAction(str(child_layer[2]), sub_menu, checkable=True) # Get load layers and create child layers menu (actions) layers_list = [] layers = self.iface.mapCanvas().layers() for layer in layers: layers_list.append(str(layer.name())) if str(child_layer[0]) in layers_list: action.setChecked(True) sub_menu.addAction(action) if child_layer[0] == 'Load all': action.triggered.connect( partial(self.add_layer.from_postgres_to_toc, child_layers=child_layers)) else: layer_name = child_layer[0] the_geom = "the_geom" geom_field = child_layer[1] + "_id" style_id = child_layer[3] group = child_layer[4] if child_layer[ 4] is not None else 'GW Layers' action.triggered.connect( partial(self.add_layer.from_postgres_to_toc, layer_name, the_geom, geom_field, None, group, style_id)) main_menu.exec_(click_point)
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameters: if param.isAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr(' (x, y)') try: if param.optional: desc += self.tr(' [optional]') except: pass widget = self.getWidgetFromParameter(param) self.valueItems[param.name] = widget if isinstance(param, ParameterVector) and \ not self.alg.allowOnlyOpenedLayers: layout = QHBoxLayout() layout.setSpacing(2) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setToolTip(self.tr('Iterate over this layer')) button.setCheckable(True) layout.addWidget(button) self.iterateButtons[param.name] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) tooltips = self.alg.getParameterDescriptions() widget.setToolTip(tooltips.get(param.name, param.description)) if isinstance(param, ParameterBoolean): widget.setText(desc) if param.isAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) else: label = QLabel(desc) #label.setToolTip(tooltip) self.labels[param.name] = label if param.isAdvanced: self.layoutAdvanced.addWidget(label) self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) self.widgets[param.name] = widget for output in self.alg.outputs: if output.hidden: continue label = QLabel(output.description) widget = OutputSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (OutputRaster, OutputVector, OutputTable)): check = QCheckBox() check.setText(self.tr('Open output file after running algorithm')) check.setChecked(True) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name] = check self.valueItems[output.name] = widget if isinstance(output, OutputVector): if output.base_input in self.dependentItems: items = self.dependentItems[output.base_input] else: items = [] self.dependentItems[output.base_input] = items items.append(output) base_input = self.alg.getParameterFromName(output.base_input) if isinstance(base_input, ParameterVector): layers = dataobjects.getVectorLayers(base_input.shapetype) else: layers = dataobjects.getTables() if len(layers) > 0: output.base_layer = layers[0]
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.isDestination(): continue else: wrapper = WidgetWrapperFactory.create_wrapper( param, self.parent) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(6) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon( os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) button.setToolTip( self. tr('Iterate over this layer, creating a separate output for every feature in the layer' )) button.setCheckable(True) layout.addWidget(button) layout.setAlignment(button, Qt.AlignTop) self.iterateButtons[param.name()] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) widget.setToolTip(param.toolTip()) if wrapper.label is not None: if param.flags( ) & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(wrapper.label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, wrapper.label) else: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr(' (x, y)') if param.flags( ) & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') widget.setText(desc) if param.flags( ) & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue label = QLabel(output.description()) widget = DestinationSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): check = QCheckBox() check.setText( QCoreApplication.translate( 'ParametersPanel', 'Open output file after running algorithm')) def skipOutputChanged(checkbox, skipped): checkbox.setEnabled(not skipped) if skipped: checkbox.setChecked(False) check.setChecked(not widget.outputIsSkipped()) check.setEnabled(not widget.outputIsSkipped()) widget.skipOutputChanged.connect( partial(skipOutputChanged, check)) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name()] = check widget.setToolTip(param.toolTip()) self.outputWidgets[output.name()] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self._variables_scope = None # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.propertiesDock.setWindowTitle(self.tr("Model Properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView(None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip(self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.variables_dock = QgsDockWidget(self) self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.variables_dock.setObjectName("variablesDock") self.variables_dock_contents = QWidget() vl_v = QVBoxLayout() vl_v.setContentsMargins(0, 0, 0, 0) self.variables_editor = QgsVariableEditorWidget() vl_v.addWidget(self.variables_editor) self.variables_dock_contents.setLayout(vl_v) self.variables_dock.setWidget(self.variables_dock_contents) self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock) self.variables_dock.setWindowTitle(self.tr("Variables")) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.tabifyDockWidget(self.propertiesDock, self.variables_dock) self.variables_editor.scopeChanged.connect(self.variables_changed) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.toolbutton_export_to_script = QToolButton() self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup) self.export_to_script_algorithm_action = QAction(QCoreApplication.translate('ModelerDialog', 'Export as Script Algorithm…')) self.toolbutton_export_to_script.addActions([self.export_to_script_algorithm_action]) self.mToolbar.insertWidget(self.mActionExportImage, self.toolbutton_export_to_script) self.export_to_script_algorithm_action.triggered.connect(self.export_as_script_algorithm) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.toolbutton_export_to_script.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): def alg_dropped(algorithm_id, pos): alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm_id) if alg is not None: self._addAlgorithm(alg, pos) else: assert False, algorithm_id def input_dropped(id, pos): if id in [param.id() for param in QgsApplication.instance().processingRegistry().parameterTypes()]: self.addInputOfType(itemId, pos) if event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data('application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() QTimer.singleShot(0, lambda id=algorithm_id, pos=self.view.mapToScene(event.pos()): alg_dropped(id, pos)) event.accept() elif event.mimeData().hasText(): itemId = event.mimeData().text() QTimer.singleShot(0, lambda id=itemId, pos=self.view.mapToScene(event.pos()): input_dropped(id, pos)) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel(self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) filters = QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterModeler) if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues self.algorithmTree.setFilters(filters) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.update_variables_gui() self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def update_variables_gui(self): variables_scope = QgsExpressionContextScope(self.tr('Model Variables')) for k, v in self.model.variables().items(): variables_scope.setVariable(k, v) variables_context = QgsExpressionContext() variables_context.appendScope(variables_scope) self.variables_editor.setContext(variables_context) self.variables_editor.setEditableScopeIndex(0) def variables_changed(self): self.model.setVariables(self.variables_editor.variablesInActiveScope()) def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", self.tr("Model doesn't contain any algorithm and/or parameter and can't be executed"), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow()) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById(PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", self.tr("Successfully exported model as image to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as PDF to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as SVG to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", self.tr("Successfully exported model as python script to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving') ) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), QCoreApplication.translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option.")) ) return self.update_model.emit() if saveAs: self.bar.pushMessage("", self.tr("Model was correctly saved to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) else: self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.update_variables_gui() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical(self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted(QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def export_as_script_algorithm(self): dlg = ScriptEditorDialog(None) dlg.editor.setText('\n'.join(self.model.asPythonCode(QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4))) dlg.show()
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.grpAdvanced.show() break widget_context = QgsProcessingParameterWidgetContext() widget_context.setProject(QgsProject.instance()) if iface is not None: widget_context.setMapCanvas(iface.mapCanvas()) if isinstance(self.alg, QgsProcessingModelAlgorithm): widget_context.setModel(self.alg) # Create widgets and put them in layouts for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.isDestination(): continue else: wrapper = WidgetWrapperFactory.create_wrapper( param, self.parent) wrapper.setWidgetContext(widget_context) wrapper.registerProcessingContextGenerator( self.context_generator) self.wrappers[param.name()] = wrapper # For compatibility with 3.x API, we need to check whether the wrapper is # the deprecated WidgetWrapper class. If not, it's the newer # QgsAbstractProcessingParameterWidgetWrapper class # TODO QGIS 4.0 - remove is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper) if not is_python_wrapper: widget = wrapper.createWrappedWidget( self.processing_context) else: widget = wrapper.widget if self.in_place and param.name() in ('INPUT', 'OUTPUT'): # don't show the input/output parameter widgets in in-place mode # we still need to CREATE them, because other wrappers may need to interact # with them (e.g. those parameters which need the input layer for field # selections/crs properties/etc) continue if widget is not None: if is_python_wrapper: widget.setToolTip(param.toolTip()) if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(6) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon( os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) button.setToolTip( self. tr('Iterate over this layer, creating a separate output for every feature in the layer' )) button.setCheckable(True) layout.addWidget(button) layout.setAlignment(button, Qt.AlignTop) self.iterateButtons[param.name()] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) label = None if not is_python_wrapper: label = wrapper.createWrappedLabel() else: label = wrapper.label if label is not None: if param.flags( ) & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) elif is_python_wrapper: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr(' (x, y)') if param.flags( ) & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') widget.setText(desc) if param.flags( ) & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if self.in_place and param.name() in ('INPUT', 'OUTPUT'): continue label = QLabel(output.description()) widget = DestinationSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): check = QCheckBox() check.setText( QCoreApplication.translate( 'ParametersPanel', 'Open output file after running algorithm')) def skipOutputChanged(checkbox, skipped): checkbox.setEnabled(not skipped) if skipped: checkbox.setChecked(False) check.setChecked(not widget.outputIsSkipped()) check.setEnabled(not widget.outputIsSkipped()) widget.skipOutputChanged.connect( partial(skipOutputChanged, check)) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name()] = check widget.setToolTip(param.toolTip()) self.outputWidgets[output.name()] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
class PythonConsoleWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle( QCoreApplication.translate("PythonConsole", "Python Console")) self.settings = QgsSettings() self.shell = ShellScintilla(self) self.setFocusProxy(self.shell) self.shellOut = ShellOutputScintilla(self) self.tabEditorWidget = EditorTabWidget(self) # ------------ UI ------------------------------- self.splitterEditor = QSplitter(self) self.splitterEditor.setOrientation(Qt.Horizontal) self.splitterEditor.setHandleWidth(6) self.splitterEditor.setChildrenCollapsible(True) self.shellOutWidget = QWidget(self) self.shellOutWidget.setLayout(QVBoxLayout()) self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0) self.shellOutWidget.layout().addWidget(self.shellOut) self.splitter = QSplitter(self.splitterEditor) self.splitter.setOrientation(Qt.Vertical) self.splitter.setHandleWidth(3) self.splitter.setChildrenCollapsible(False) self.splitter.addWidget(self.shellOutWidget) self.splitter.addWidget(self.shell) # self.splitterEditor.addWidget(self.tabEditorWidget) self.splitterObj = QSplitter(self.splitterEditor) self.splitterObj.setHandleWidth(3) self.splitterObj.setOrientation(Qt.Horizontal) # self.splitterObj.setSizes([0, 0]) # self.splitterObj.setStretchFactor(0, 1) self.widgetEditor = QWidget(self.splitterObj) self.widgetFind = QWidget(self) self.listClassMethod = QTreeWidget(self.splitterObj) self.listClassMethod.setColumnCount(2) objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector") self.listClassMethod.setHeaderLabels([objInspLabel, '']) self.listClassMethod.setColumnHidden(1, True) self.listClassMethod.setAlternatingRowColors(True) # self.splitterEditor.addWidget(self.widgetEditor) # self.splitterObj.addWidget(self.listClassMethod) # self.splitterObj.addWidget(self.widgetEditor) # Hide side editor on start up self.splitterObj.hide() self.listClassMethod.hide() # Hide search widget on start up self.widgetFind.hide() icon_size = iface.iconSize( dockedToolbar=True) if iface else QSize(16, 16) sizes = self.splitter.sizes() self.splitter.setSizes(sizes) # ----------------Restore Settings------------------------------------ self.restoreSettingsConsole() # ------------------Toolbar Editor------------------------------------- # Action for Open File openFileBt = QCoreApplication.translate("PythonConsole", "Open Script...") self.openFileButton = QAction(self) self.openFileButton.setCheckable(False) self.openFileButton.setEnabled(True) self.openFileButton.setIcon( QgsApplication.getThemeIcon("console/iconOpenConsole.png")) self.openFileButton.setMenuRole(QAction.PreferencesRole) self.openFileButton.setIconVisibleInMenu(True) self.openFileButton.setToolTip(openFileBt) self.openFileButton.setText(openFileBt) openExtEditorBt = QCoreApplication.translate( "PythonConsole", "Open in External Editor") self.openInEditorButton = QAction(self) self.openInEditorButton.setCheckable(False) self.openInEditorButton.setEnabled(True) self.openInEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.openInEditorButton.setMenuRole(QAction.PreferencesRole) self.openInEditorButton.setIconVisibleInMenu(True) self.openInEditorButton.setToolTip(openExtEditorBt) self.openInEditorButton.setText(openExtEditorBt) # Action for Save File saveFileBt = QCoreApplication.translate("PythonConsole", "Save") self.saveFileButton = QAction(self) self.saveFileButton.setCheckable(False) self.saveFileButton.setEnabled(False) self.saveFileButton.setIcon( QgsApplication.getThemeIcon("console/iconSaveConsole.png")) self.saveFileButton.setMenuRole(QAction.PreferencesRole) self.saveFileButton.setIconVisibleInMenu(True) self.saveFileButton.setToolTip(saveFileBt) self.saveFileButton.setText(saveFileBt) # Action for Save File As saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As...") self.saveAsFileButton = QAction(self) self.saveAsFileButton.setCheckable(False) self.saveAsFileButton.setEnabled(True) self.saveAsFileButton.setIcon( QgsApplication.getThemeIcon("console/iconSaveAsConsole.png")) self.saveAsFileButton.setMenuRole(QAction.PreferencesRole) self.saveAsFileButton.setIconVisibleInMenu(True) self.saveAsFileButton.setToolTip(saveAsFileBt) self.saveAsFileButton.setText(saveAsFileBt) # Action Cut cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") self.cutEditorButton = QAction(self) self.cutEditorButton.setCheckable(False) self.cutEditorButton.setEnabled(True) self.cutEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cutEditorButton.setMenuRole(QAction.PreferencesRole) self.cutEditorButton.setIconVisibleInMenu(True) self.cutEditorButton.setToolTip(cutEditorBt) self.cutEditorButton.setText(cutEditorBt) # Action Copy copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") self.copyEditorButton = QAction(self) self.copyEditorButton.setCheckable(False) self.copyEditorButton.setEnabled(True) self.copyEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditCopy.svg")) self.copyEditorButton.setMenuRole(QAction.PreferencesRole) self.copyEditorButton.setIconVisibleInMenu(True) self.copyEditorButton.setToolTip(copyEditorBt) self.copyEditorButton.setText(copyEditorBt) # Action Paste pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") self.pasteEditorButton = QAction(self) self.pasteEditorButton.setCheckable(False) self.pasteEditorButton.setEnabled(True) self.pasteEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditPaste.svg")) self.pasteEditorButton.setMenuRole(QAction.PreferencesRole) self.pasteEditorButton.setIconVisibleInMenu(True) self.pasteEditorButton.setToolTip(pasteEditorBt) self.pasteEditorButton.setText(pasteEditorBt) # Action Run Script (subprocess) runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run script") self.runScriptEditorButton = QAction(self) self.runScriptEditorButton.setCheckable(False) self.runScriptEditorButton.setEnabled(True) self.runScriptEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconRunScriptConsole.png")) self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole) self.runScriptEditorButton.setIconVisibleInMenu(True) self.runScriptEditorButton.setToolTip(runScriptEditorBt) self.runScriptEditorButton.setText(runScriptEditorBt) # Action Run Script (subprocess) commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment") self.commentEditorButton = QAction(self) self.commentEditorButton.setCheckable(False) self.commentEditorButton.setEnabled(True) self.commentEditorButton.setIcon( QgsApplication.getThemeIcon( "console/iconCommentEditorConsole.png")) self.commentEditorButton.setMenuRole(QAction.PreferencesRole) self.commentEditorButton.setIconVisibleInMenu(True) self.commentEditorButton.setToolTip(commentEditorBt) self.commentEditorButton.setText(commentEditorBt) # Action Run Script (subprocess) uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment") self.uncommentEditorButton = QAction(self) self.uncommentEditorButton.setCheckable(False) self.uncommentEditorButton.setEnabled(True) self.uncommentEditorButton.setIcon( QgsApplication.getThemeIcon( "console/iconUncommentEditorConsole.png")) self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole) self.uncommentEditorButton.setIconVisibleInMenu(True) self.uncommentEditorButton.setToolTip(uncommentEditorBt) self.uncommentEditorButton.setText(uncommentEditorBt) # Action for Object browser objList = QCoreApplication.translate("PythonConsole", "Object Inspector...") self.objectListButton = QAction(self) self.objectListButton.setCheckable(True) self.objectListButton.setEnabled( self.settings.value("pythonConsole/enableObjectInsp", False, type=bool)) self.objectListButton.setIcon( QgsApplication.getThemeIcon("console/iconClassBrowserConsole.png")) self.objectListButton.setMenuRole(QAction.PreferencesRole) self.objectListButton.setIconVisibleInMenu(True) self.objectListButton.setToolTip(objList) self.objectListButton.setText(objList) # Action for Find text findText = QCoreApplication.translate("PythonConsole", "Find Text") self.findTextButton = QAction(self) self.findTextButton.setCheckable(True) self.findTextButton.setEnabled(True) self.findTextButton.setIcon( QgsApplication.getThemeIcon("console/iconSearchEditorConsole.png")) self.findTextButton.setMenuRole(QAction.PreferencesRole) self.findTextButton.setIconVisibleInMenu(True) self.findTextButton.setToolTip(findText) self.findTextButton.setText(findText) # ----------------Toolbar Console------------------------------------- # Action Show Editor showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") self.showEditorButton = QAction(self) self.showEditorButton.setEnabled(True) self.showEditorButton.setCheckable(True) self.showEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.showEditorButton.setMenuRole(QAction.PreferencesRole) self.showEditorButton.setIconVisibleInMenu(True) self.showEditorButton.setToolTip(showEditor) self.showEditorButton.setText(showEditor) # Action for Clear button clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") self.clearButton = QAction(self) self.clearButton.setCheckable(False) self.clearButton.setEnabled(True) self.clearButton.setIcon( QgsApplication.getThemeIcon("console/iconClearConsole.png")) self.clearButton.setMenuRole(QAction.PreferencesRole) self.clearButton.setIconVisibleInMenu(True) self.clearButton.setToolTip(clearBt) self.clearButton.setText(clearBt) # Action for settings optionsBt = QCoreApplication.translate("PythonConsole", "Options...") self.optionsButton = QAction(self) self.optionsButton.setCheckable(False) self.optionsButton.setEnabled(True) self.optionsButton.setIcon( QgsApplication.getThemeIcon("console/iconSettingsConsole.png")) self.optionsButton.setMenuRole(QAction.PreferencesRole) self.optionsButton.setIconVisibleInMenu(True) self.optionsButton.setToolTip(optionsBt) self.optionsButton.setText(optionsBt) # Action for Run script runBt = QCoreApplication.translate("PythonConsole", "Run Command") self.runButton = QAction(self) self.runButton.setCheckable(False) self.runButton.setEnabled(True) self.runButton.setIcon( QgsApplication.getThemeIcon("console/iconRunConsole.png")) self.runButton.setMenuRole(QAction.PreferencesRole) self.runButton.setIconVisibleInMenu(True) self.runButton.setToolTip(runBt) self.runButton.setText(runBt) # Help action helpBt = QCoreApplication.translate("PythonConsole", "Help...") self.helpButton = QAction(self) self.helpButton.setCheckable(False) self.helpButton.setEnabled(True) self.helpButton.setIcon( QgsApplication.getThemeIcon("console/iconHelpConsole.png")) self.helpButton.setMenuRole(QAction.PreferencesRole) self.helpButton.setIconVisibleInMenu(True) self.helpButton.setToolTip(helpBt) self.helpButton.setText(helpBt) self.toolBar = QToolBar() self.toolBar.setEnabled(True) self.toolBar.setFocusPolicy(Qt.NoFocus) self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBar.setLayoutDirection(Qt.LeftToRight) self.toolBar.setIconSize(icon_size) self.toolBar.setMovable(False) self.toolBar.setFloatable(False) self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.runButton) self.toolBar.addSeparator() self.toolBar.addAction(self.showEditorButton) self.toolBar.addSeparator() self.toolBar.addAction(self.optionsButton) self.toolBar.addAction(self.helpButton) self.toolBarEditor = QToolBar() self.toolBarEditor.setEnabled(False) self.toolBarEditor.setFocusPolicy(Qt.NoFocus) self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBarEditor.setLayoutDirection(Qt.LeftToRight) self.toolBarEditor.setIconSize(icon_size) self.toolBarEditor.setMovable(False) self.toolBarEditor.setFloatable(False) self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.findTextButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.commentEditorButton) self.toolBarEditor.addAction(self.uncommentEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.objectListButton) self.widgetButton = QWidget() sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.widgetButton.sizePolicy().hasHeightForWidth()) self.widgetButton.setSizePolicy(sizePolicy) self.widgetButtonEditor = QWidget(self.widgetEditor) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.widgetButtonEditor.sizePolicy().hasHeightForWidth()) self.widgetButtonEditor.setSizePolicy(sizePolicy) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.shellOut.sizePolicy().hasHeightForWidth()) self.shellOut.setSizePolicy(sizePolicy) self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # ------------ Layout ------------------------------- self.mainLayout = QGridLayout(self) self.mainLayout.setMargin(0) self.mainLayout.setSpacing(0) self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1) self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1) self.shellOutWidget.layout().insertWidget(0, self.toolBar) self.layoutEditor = QGridLayout(self.widgetEditor) self.layoutEditor.setMargin(0) self.layoutEditor.setSpacing(0) self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1) self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1) self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1) self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1) # Layout for the find widget self.layoutFind = QGridLayout(self.widgetFind) self.layoutFind.setContentsMargins(0, 0, 0, 0) self.lineEditFind = QgsFilterLineEdit() placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find...") self.lineEditFind.setPlaceholderText(placeHolderTxt) self.findNextButton = QToolButton() self.findNextButton.setEnabled(False) toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next") self.findNextButton.setToolTip(toolTipfindNext) self.findNextButton.setIcon( QgsApplication.getThemeIcon( "console/iconSearchNextEditorConsole.png")) self.findNextButton.setIconSize(QSize(24, 24)) self.findNextButton.setAutoRaise(True) self.findPrevButton = QToolButton() self.findPrevButton.setEnabled(False) toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous") self.findPrevButton.setToolTip(toolTipfindPrev) self.findPrevButton.setIcon( QgsApplication.getThemeIcon( "console/iconSearchPrevEditorConsole.png")) self.findPrevButton.setIconSize(QSize(24, 24)) self.findPrevButton.setAutoRaise(True) self.caseSensitive = QCheckBox() caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive") self.caseSensitive.setText(caseSensTr) self.wholeWord = QCheckBox() wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word") self.wholeWord.setText(wholeWordTr) self.wrapAround = QCheckBox() self.wrapAround.setChecked(True) wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around") self.wrapAround.setText(wrapAroundTr) self.layoutFind.addWidget(self.lineEditFind, 0, 1, 1, 1) self.layoutFind.addWidget(self.findPrevButton, 0, 2, 1, 1) self.layoutFind.addWidget(self.findNextButton, 0, 3, 1, 1) self.layoutFind.addWidget(self.caseSensitive, 0, 4, 1, 1) self.layoutFind.addWidget(self.wholeWord, 0, 5, 1, 1) self.layoutFind.addWidget(self.wrapAround, 0, 6, 1, 1) # ------------ Add first Tab in Editor ------------------------------- # self.tabEditorWidget.newTabEditor(tabName='first', filename=None) # ------------ Signal ------------------------------- self.findTextButton.triggered.connect(self._toggleFind) self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.commentEditorButton.triggered.connect(self.commentCode) self.uncommentEditorButton.triggered.connect(self.uncommentCode) self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.cutEditorButton.triggered.connect(self.cutEditor) self.copyEditorButton.triggered.connect(self.copyEditor) self.pasteEditorButton.triggered.connect(self.pasteEditor) self.showEditorButton.toggled.connect(self.toggleEditor) self.clearButton.triggered.connect(self.shellOut.clearConsole) self.optionsButton.triggered.connect(self.openSettings) self.runButton.triggered.connect(self.shell.entered) self.openFileButton.triggered.connect(self.openScriptFile) self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.saveFileButton.triggered.connect(self.saveScriptFile) self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.helpButton.triggered.connect(self.openHelp) self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.lineEditFind.returnPressed.connect(self._findNext) self.findNextButton.clicked.connect(self._findNext) self.findPrevButton.clicked.connect(self._findPrev) self.lineEditFind.textChanged.connect(self._textFindChanged) self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._openFind) self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor) self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut) self.findNextScut.activated.connect(self._findNext) self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor) self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut) self.findPreviousScut.activated.connect(self._findPrev) # Escape on editor hides the find bar self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._closeFind) def _toggleFind(self): self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget() def _openFind(self): self.tabEditorWidget.currentWidget().newEditor.openFindWidget() def _closeFind(self): self.tabEditorWidget.currentWidget().newEditor.closeFindWidget() def _findNext(self): self.tabEditorWidget.currentWidget().newEditor.findText(True) def _findPrev(self): self.tabEditorWidget.currentWidget().newEditor.findText(False) def _textFindChanged(self): if self.lineEditFind.text(): self.findNextButton.setEnabled(True) self.findPrevButton.setEnabled(True) self.tabEditorWidget.currentWidget().newEditor.findText( True, showMessage=False, findFirst=True) else: self.lineEditFind.setStyleSheet('') self.findNextButton.setEnabled(False) self.findPrevButton.setEnabled(False) def onClickGoToLine(self, item, column): tabEditor = self.tabEditorWidget.currentWidget().newEditor if item.text(1) == 'syntaxError': check = tabEditor.syntaxCheck(fromContextMenu=False) if check and not tabEditor.isReadOnly(): self.tabEditorWidget.currentWidget().save() return linenr = int(item.text(1)) itemName = str(item.text(0)) charPos = itemName.find(' ') if charPos != -1: objName = itemName[0:charPos] else: objName = itemName tabEditor.goToLine(objName, linenr) def toggleEditor(self, checked): self.splitterObj.show() if checked else self.splitterObj.hide() if not self.tabEditorWidget: self.tabEditorWidget.enableToolBarEditor(checked) self.tabEditorWidget.restoreTabsOrAddNew() def toggleObjectListWidget(self, checked): self.listClassMethod.show() if checked else self.listClassMethod.hide() def pasteEditor(self): self.tabEditorWidget.currentWidget().newEditor.paste() def cutEditor(self): self.tabEditorWidget.currentWidget().newEditor.cut() def copyEditor(self): self.tabEditorWidget.currentWidget().newEditor.copy() def runScriptEditor(self): self.tabEditorWidget.currentWidget().newEditor.runScriptCode() def commentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True) def uncommentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False) def openScriptFileExtEditor(self): tabWidget = self.tabEditorWidget.currentWidget() path = tabWidget.path import subprocess try: subprocess.Popen([os.environ['EDITOR'], path]) except KeyError: QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def openScriptFile(self): lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) openFileTr = QCoreApplication.translate("PythonConsole", "Open File") fileList, selected_filter = QFileDialog.getOpenFileNames( self, openFileTr, lastDirPath, "Script file (*.py)") if fileList: for pyFile in fileList: for i in range(self.tabEditorWidget.count()): tabWidget = self.tabEditorWidget.widget(i) if tabWidget.path == pyFile: self.tabEditorWidget.setCurrentWidget(tabWidget) break else: tabName = QFileInfo(pyFile).fileName() self.tabEditorWidget.newTabEditor(tabName, pyFile) lastDirPath = QFileInfo(pyFile).path() self.settings.setValue("pythonConsole/lastDirPath", pyFile) self.updateTabListScript(pyFile, action='append') def saveScriptFile(self): tabWidget = self.tabEditorWidget.currentWidget() try: tabWidget.save() except (IOError, OSError) as error: msgText = QCoreApplication.translate( 'PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format( tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) def saveAsScriptFile(self, index=None): tabWidget = self.tabEditorWidget.currentWidget() if not index: index = self.tabEditorWidget.currentIndex() if not tabWidget.path: fileName = self.tabEditorWidget.tabText(index) + '.py' folder = self.settings.value("pythonConsole/lastDirPath", QDir.home()) pathFileName = os.path.join(folder, fileName) fileNone = True else: pathFileName = tabWidget.path fileNone = False saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As") filename, filter = QFileDialog.getSaveFileName(self, saveAsFileTr, pathFileName, "Script file (*.py)") if filename: try: tabWidget.save(filename) except (IOError, OSError) as error: msgText = QCoreApplication.translate( 'PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}' ).format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) if fileNone: tabWidget.path = None else: tabWidget.path = pathFileName return if not fileNone: self.updateTabListScript(pathFileName, action='remove') def openHelp(self): QgsHelp.openHelp("plugins/python_console.html") def openSettings(self): if optionsDialog(self).exec_(): self.shell.refreshSettingsShell() self.shellOut.refreshSettingsOutput() self.tabEditorWidget.refreshSettingsEditor() def callWidgetMessageBar(self, text): self.shellOut.widgetMessageBar(iface, text) def callWidgetMessageBarEditor(self, text, level, timed): self.tabEditorWidget.widgetMessageBar(iface, text, level, timed) def updateTabListScript(self, script, action=None): if action == 'remove': self.tabListScript.remove(script) elif action == 'append': if not self.tabListScript: self.tabListScript = [] if script not in self.tabListScript: self.tabListScript.append(script) else: self.tabListScript = [] self.settings.setValue("pythonConsole/tabScripts", self.tabListScript) def saveSettingsConsole(self): self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState()) self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState()) self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState()) self.shell.writeHistoryFile(True) def restoreSettingsConsole(self): storedTabScripts = self.settings.value("pythonConsole/tabScripts", []) self.tabListScript = storedTabScripts self.splitter.restoreState( self.settings.value("pythonConsole/splitterConsole", QByteArray())) self.splitterEditor.restoreState( self.settings.value("pythonConsole/splitterEditor", QByteArray())) self.splitterObj.restoreState( self.settings.value("pythonConsole/splitterObj", QByteArray()))
class PythonConsoleWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console")) self.settings = QgsSettings() self.shell = ShellScintilla(self) self.setFocusProxy(self.shell) self.shellOut = ShellOutputScintilla(self) self.tabEditorWidget = EditorTabWidget(self) # ------------ UI ------------------------------- self.splitterEditor = QSplitter(self) self.splitterEditor.setOrientation(Qt.Horizontal) self.splitterEditor.setHandleWidth(6) self.splitterEditor.setChildrenCollapsible(True) self.shellOutWidget = QWidget(self) self.shellOutWidget.setLayout(QVBoxLayout()) self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0) self.shellOutWidget.layout().addWidget(self.shellOut) self.splitter = QSplitter(self.splitterEditor) self.splitter.setOrientation(Qt.Vertical) self.splitter.setHandleWidth(3) self.splitter.setChildrenCollapsible(False) self.splitter.addWidget(self.shellOutWidget) self.splitter.addWidget(self.shell) # self.splitterEditor.addWidget(self.tabEditorWidget) self.splitterObj = QSplitter(self.splitterEditor) self.splitterObj.setHandleWidth(3) self.splitterObj.setOrientation(Qt.Horizontal) # self.splitterObj.setSizes([0, 0]) # self.splitterObj.setStretchFactor(0, 1) self.widgetEditor = QWidget(self.splitterObj) self.widgetFind = QWidget(self) self.listClassMethod = QTreeWidget(self.splitterObj) self.listClassMethod.setColumnCount(2) objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector") self.listClassMethod.setHeaderLabels([objInspLabel, '']) self.listClassMethod.setColumnHidden(1, True) self.listClassMethod.setAlternatingRowColors(True) # self.splitterEditor.addWidget(self.widgetEditor) # self.splitterObj.addWidget(self.listClassMethod) # self.splitterObj.addWidget(self.widgetEditor) # Hide side editor on start up self.splitterObj.hide() self.listClassMethod.hide() # Hide search widget on start up self.widgetFind.hide() sizes = self.splitter.sizes() self.splitter.setSizes(sizes) # ----------------Restore Settings------------------------------------ self.restoreSettingsConsole() # ------------------Toolbar Editor------------------------------------- # Action for Open File openFileBt = QCoreApplication.translate("PythonConsole", "Open Script...") self.openFileButton = QAction(self) self.openFileButton.setCheckable(False) self.openFileButton.setEnabled(True) self.openFileButton.setIcon(QgsApplication.getThemeIcon("console/iconOpenConsole.png")) self.openFileButton.setMenuRole(QAction.PreferencesRole) self.openFileButton.setIconVisibleInMenu(True) self.openFileButton.setToolTip(openFileBt) self.openFileButton.setText(openFileBt) openExtEditorBt = QCoreApplication.translate("PythonConsole", "Open in External Editor") self.openInEditorButton = QAction(self) self.openInEditorButton.setCheckable(False) self.openInEditorButton.setEnabled(True) self.openInEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.openInEditorButton.setMenuRole(QAction.PreferencesRole) self.openInEditorButton.setIconVisibleInMenu(True) self.openInEditorButton.setToolTip(openExtEditorBt) self.openInEditorButton.setText(openExtEditorBt) # Action for Save File saveFileBt = QCoreApplication.translate("PythonConsole", "Save") self.saveFileButton = QAction(self) self.saveFileButton.setCheckable(False) self.saveFileButton.setEnabled(False) self.saveFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveConsole.png")) self.saveFileButton.setMenuRole(QAction.PreferencesRole) self.saveFileButton.setIconVisibleInMenu(True) self.saveFileButton.setToolTip(saveFileBt) self.saveFileButton.setText(saveFileBt) # Action for Save File As saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As...") self.saveAsFileButton = QAction(self) self.saveAsFileButton.setCheckable(False) self.saveAsFileButton.setEnabled(True) self.saveAsFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveAsConsole.png")) self.saveAsFileButton.setMenuRole(QAction.PreferencesRole) self.saveAsFileButton.setIconVisibleInMenu(True) self.saveAsFileButton.setToolTip(saveAsFileBt) self.saveAsFileButton.setText(saveAsFileBt) # Action Cut cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") self.cutEditorButton = QAction(self) self.cutEditorButton.setCheckable(False) self.cutEditorButton.setEnabled(True) self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cutEditorButton.setMenuRole(QAction.PreferencesRole) self.cutEditorButton.setIconVisibleInMenu(True) self.cutEditorButton.setToolTip(cutEditorBt) self.cutEditorButton.setText(cutEditorBt) # Action Copy copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") self.copyEditorButton = QAction(self) self.copyEditorButton.setCheckable(False) self.copyEditorButton.setEnabled(True) self.copyEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg")) self.copyEditorButton.setMenuRole(QAction.PreferencesRole) self.copyEditorButton.setIconVisibleInMenu(True) self.copyEditorButton.setToolTip(copyEditorBt) self.copyEditorButton.setText(copyEditorBt) # Action Paste pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") self.pasteEditorButton = QAction(self) self.pasteEditorButton.setCheckable(False) self.pasteEditorButton.setEnabled(True) self.pasteEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg")) self.pasteEditorButton.setMenuRole(QAction.PreferencesRole) self.pasteEditorButton.setIconVisibleInMenu(True) self.pasteEditorButton.setToolTip(pasteEditorBt) self.pasteEditorButton.setText(pasteEditorBt) # Action Run Script (subprocess) runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run script") self.runScriptEditorButton = QAction(self) self.runScriptEditorButton.setCheckable(False) self.runScriptEditorButton.setEnabled(True) self.runScriptEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconRunScriptConsole.png")) self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole) self.runScriptEditorButton.setIconVisibleInMenu(True) self.runScriptEditorButton.setToolTip(runScriptEditorBt) self.runScriptEditorButton.setText(runScriptEditorBt) # Action Run Script (subprocess) commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment") self.commentEditorButton = QAction(self) self.commentEditorButton.setCheckable(False) self.commentEditorButton.setEnabled(True) self.commentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.png")) self.commentEditorButton.setMenuRole(QAction.PreferencesRole) self.commentEditorButton.setIconVisibleInMenu(True) self.commentEditorButton.setToolTip(commentEditorBt) self.commentEditorButton.setText(commentEditorBt) # Action Run Script (subprocess) uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment") self.uncommentEditorButton = QAction(self) self.uncommentEditorButton.setCheckable(False) self.uncommentEditorButton.setEnabled(True) self.uncommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.png")) self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole) self.uncommentEditorButton.setIconVisibleInMenu(True) self.uncommentEditorButton.setToolTip(uncommentEditorBt) self.uncommentEditorButton.setText(uncommentEditorBt) # Action for Object browser objList = QCoreApplication.translate("PythonConsole", "Object Inspector...") self.objectListButton = QAction(self) self.objectListButton.setCheckable(True) self.objectListButton.setEnabled(self.settings.value("pythonConsole/enableObjectInsp", False, type=bool)) self.objectListButton.setIcon(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.png")) self.objectListButton.setMenuRole(QAction.PreferencesRole) self.objectListButton.setIconVisibleInMenu(True) self.objectListButton.setToolTip(objList) self.objectListButton.setText(objList) # Action for Find text findText = QCoreApplication.translate("PythonConsole", "Find Text") self.findTextButton = QAction(self) self.findTextButton.setCheckable(True) self.findTextButton.setEnabled(True) self.findTextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchEditorConsole.png")) self.findTextButton.setMenuRole(QAction.PreferencesRole) self.findTextButton.setIconVisibleInMenu(True) self.findTextButton.setToolTip(findText) self.findTextButton.setText(findText) # ----------------Toolbar Console------------------------------------- # Action Show Editor showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") self.showEditorButton = QAction(self) self.showEditorButton.setEnabled(True) self.showEditorButton.setCheckable(True) self.showEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.showEditorButton.setMenuRole(QAction.PreferencesRole) self.showEditorButton.setIconVisibleInMenu(True) self.showEditorButton.setToolTip(showEditor) self.showEditorButton.setText(showEditor) # Action for Clear button clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") self.clearButton = QAction(self) self.clearButton.setCheckable(False) self.clearButton.setEnabled(True) self.clearButton.setIcon(QgsApplication.getThemeIcon("console/iconClearConsole.png")) self.clearButton.setMenuRole(QAction.PreferencesRole) self.clearButton.setIconVisibleInMenu(True) self.clearButton.setToolTip(clearBt) self.clearButton.setText(clearBt) # Action for settings optionsBt = QCoreApplication.translate("PythonConsole", "Options...") self.optionsButton = QAction(self) self.optionsButton.setCheckable(False) self.optionsButton.setEnabled(True) self.optionsButton.setIcon(QgsApplication.getThemeIcon("console/iconSettingsConsole.png")) self.optionsButton.setMenuRole(QAction.PreferencesRole) self.optionsButton.setIconVisibleInMenu(True) self.optionsButton.setToolTip(optionsBt) self.optionsButton.setText(optionsBt) # Action menu for class actionClassBt = QCoreApplication.translate("PythonConsole", "Import Class") self.actionClass = QAction(self) self.actionClass.setCheckable(False) self.actionClass.setEnabled(True) self.actionClass.setIcon(QgsApplication.getThemeIcon("console/iconClassConsole.png")) self.actionClass.setMenuRole(QAction.PreferencesRole) self.actionClass.setIconVisibleInMenu(True) self.actionClass.setToolTip(actionClassBt) self.actionClass.setText(actionClassBt) # Action for Run script runBt = QCoreApplication.translate("PythonConsole", "Run Command") self.runButton = QAction(self) self.runButton.setCheckable(False) self.runButton.setEnabled(True) self.runButton.setIcon(QgsApplication.getThemeIcon("console/iconRunConsole.png")) self.runButton.setMenuRole(QAction.PreferencesRole) self.runButton.setIconVisibleInMenu(True) self.runButton.setToolTip(runBt) self.runButton.setText(runBt) # Help action helpBt = QCoreApplication.translate("PythonConsole", "Help...") self.helpButton = QAction(self) self.helpButton.setCheckable(False) self.helpButton.setEnabled(True) self.helpButton.setIcon(QgsApplication.getThemeIcon("console/iconHelpConsole.png")) self.helpButton.setMenuRole(QAction.PreferencesRole) self.helpButton.setIconVisibleInMenu(True) self.helpButton.setToolTip(helpBt) self.helpButton.setText(helpBt) self.toolBar = QToolBar() self.toolBar.setEnabled(True) self.toolBar.setFocusPolicy(Qt.NoFocus) self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBar.setLayoutDirection(Qt.LeftToRight) self.toolBar.setIconSize(QSize(16, 16)) self.toolBar.setMovable(False) self.toolBar.setFloatable(False) self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.actionClass) self.toolBar.addAction(self.runButton) self.toolBar.addSeparator() self.toolBar.addAction(self.showEditorButton) self.toolBar.addSeparator() self.toolBar.addAction(self.optionsButton) self.toolBar.addAction(self.helpButton) self.toolBarEditor = QToolBar() self.toolBarEditor.setEnabled(False) self.toolBarEditor.setFocusPolicy(Qt.NoFocus) self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBarEditor.setLayoutDirection(Qt.LeftToRight) self.toolBarEditor.setIconSize(QSize(16, 16)) self.toolBarEditor.setMovable(False) self.toolBarEditor.setFloatable(False) self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.findTextButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.commentEditorButton) self.toolBarEditor.addAction(self.uncommentEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.objectListButton) # Menu Import Class default_command = { (QCoreApplication.translate("PythonConsole", "Import Processing Class"), QgsApplication.getThemeIcon("console/iconProcessingConsole.png")): ["import processing"], (QCoreApplication.translate("PythonConsole", "Import PyQt.QtCore Class"), QgsApplication.getThemeIcon("console/iconQtCoreConsole.png")): ["from qgis.PyQt.QtCore import *"], (QCoreApplication.translate("PythonConsole", "Import PyQt.QtGui Class"), QgsApplication.getThemeIcon("console/iconQtGuiConsole.png")): ["from qgis.PyQt.QtGui import *", "from qgis.PyQt.QtWidgets import *"] } self.classMenu = QMenu() for (title, icon), commands in list(default_command.items()): action = self.classMenu.addAction(icon, title) action.triggered.connect(partial(self.shell.commandConsole, commands)) cM = self.toolBar.widgetForAction(self.actionClass) cM.setMenu(self.classMenu) cM.setPopupMode(QToolButton.InstantPopup) self.widgetButton = QWidget() sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButton.sizePolicy().hasHeightForWidth()) self.widgetButton.setSizePolicy(sizePolicy) self.widgetButtonEditor = QWidget(self.widgetEditor) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButtonEditor.sizePolicy().hasHeightForWidth()) self.widgetButtonEditor.setSizePolicy(sizePolicy) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.shellOut.sizePolicy().hasHeightForWidth()) self.shellOut.setSizePolicy(sizePolicy) self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # ------------ Layout ------------------------------- self.mainLayout = QGridLayout(self) self.mainLayout.setMargin(0) self.mainLayout.setSpacing(0) self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1) self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1) self.shellOutWidget.layout().insertWidget(0, self.toolBar) self.layoutEditor = QGridLayout(self.widgetEditor) self.layoutEditor.setMargin(0) self.layoutEditor.setSpacing(0) self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1) self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1) self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1) self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1) # Layout for the find widget self.layoutFind = QGridLayout(self.widgetFind) self.layoutFind.setContentsMargins(0, 0, 0, 0) self.lineEditFind = QgsFilterLineEdit() placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find...") self.lineEditFind.setPlaceholderText(placeHolderTxt) self.findNextButton = QToolButton() self.findNextButton.setEnabled(False) toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next") self.findNextButton.setToolTip(toolTipfindNext) self.findNextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchNextEditorConsole.png")) self.findNextButton.setIconSize(QSize(24, 24)) self.findNextButton.setAutoRaise(True) self.findPrevButton = QToolButton() self.findPrevButton.setEnabled(False) toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous") self.findPrevButton.setToolTip(toolTipfindPrev) self.findPrevButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchPrevEditorConsole.png")) self.findPrevButton.setIconSize(QSize(24, 24)) self.findPrevButton.setAutoRaise(True) self.caseSensitive = QCheckBox() caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive") self.caseSensitive.setText(caseSensTr) self.wholeWord = QCheckBox() wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word") self.wholeWord.setText(wholeWordTr) self.wrapAround = QCheckBox() self.wrapAround.setChecked(True) wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around") self.wrapAround.setText(wrapAroundTr) self.layoutFind.addWidget(self.lineEditFind, 0, 1, 1, 1) self.layoutFind.addWidget(self.findPrevButton, 0, 2, 1, 1) self.layoutFind.addWidget(self.findNextButton, 0, 3, 1, 1) self.layoutFind.addWidget(self.caseSensitive, 0, 4, 1, 1) self.layoutFind.addWidget(self.wholeWord, 0, 5, 1, 1) self.layoutFind.addWidget(self.wrapAround, 0, 6, 1, 1) # ------------ Add first Tab in Editor ------------------------------- # self.tabEditorWidget.newTabEditor(tabName='first', filename=None) # ------------ Signal ------------------------------- self.findTextButton.triggered.connect(self._toggleFind) self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.commentEditorButton.triggered.connect(self.commentCode) self.uncommentEditorButton.triggered.connect(self.uncommentCode) self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.cutEditorButton.triggered.connect(self.cutEditor) self.copyEditorButton.triggered.connect(self.copyEditor) self.pasteEditorButton.triggered.connect(self.pasteEditor) self.showEditorButton.toggled.connect(self.toggleEditor) self.clearButton.triggered.connect(self.shellOut.clearConsole) self.optionsButton.triggered.connect(self.openSettings) self.runButton.triggered.connect(self.shell.entered) self.openFileButton.triggered.connect(self.openScriptFile) self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.saveFileButton.triggered.connect(self.saveScriptFile) self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.helpButton.triggered.connect(self.openHelp) self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.lineEditFind.returnPressed.connect(self._findNext) self.findNextButton.clicked.connect(self._findNext) self.findPrevButton.clicked.connect(self._findPrev) self.lineEditFind.textChanged.connect(self._textFindChanged) self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._openFind) self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor) self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut) self.findNextScut.activated.connect(self._findNext) self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor) self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut) self.findPreviousScut.activated.connect(self._findPrev) # Escape on editor hides the find bar self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._closeFind) def _toggleFind(self): self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget() def _openFind(self): self.tabEditorWidget.currentWidget().newEditor.openFindWidget() def _closeFind(self): self.tabEditorWidget.currentWidget().newEditor.closeFindWidget() def _findNext(self): self.tabEditorWidget.currentWidget().newEditor.findText(True) def _findPrev(self): self.tabEditorWidget.currentWidget().newEditor.findText(False) def _textFindChanged(self): if self.lineEditFind.text(): self.findNextButton.setEnabled(True) self.findPrevButton.setEnabled(True) self.tabEditorWidget.currentWidget().newEditor.findText(True, showMessage=False, findFirst=True) else: self.lineEditFind.setStyleSheet('') self.findNextButton.setEnabled(False) self.findPrevButton.setEnabled(False) def onClickGoToLine(self, item, column): tabEditor = self.tabEditorWidget.currentWidget().newEditor if item.text(1) == 'syntaxError': check = tabEditor.syntaxCheck(fromContextMenu=False) if check and not tabEditor.isReadOnly(): self.tabEditorWidget.currentWidget().save() return linenr = int(item.text(1)) itemName = str(item.text(0)) charPos = itemName.find(' ') if charPos != -1: objName = itemName[0:charPos] else: objName = itemName tabEditor.goToLine(objName, linenr) def toggleEditor(self, checked): self.splitterObj.show() if checked else self.splitterObj.hide() if not self.tabEditorWidget: self.tabEditorWidget.enableToolBarEditor(checked) self.tabEditorWidget.restoreTabsOrAddNew() def toggleObjectListWidget(self, checked): self.listClassMethod.show() if checked else self.listClassMethod.hide() def pasteEditor(self): self.tabEditorWidget.currentWidget().newEditor.paste() def cutEditor(self): self.tabEditorWidget.currentWidget().newEditor.cut() def copyEditor(self): self.tabEditorWidget.currentWidget().newEditor.copy() def runScriptEditor(self): self.tabEditorWidget.currentWidget().newEditor.runScriptCode() def commentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True) def uncommentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False) def openScriptFileExtEditor(self): tabWidget = self.tabEditorWidget.currentWidget() path = tabWidget.path import subprocess try: subprocess.Popen([os.environ['EDITOR'], path]) except KeyError: QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def openScriptFile(self): lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) openFileTr = QCoreApplication.translate("PythonConsole", "Open File") fileList, selected_filter = QFileDialog.getOpenFileNames( self, openFileTr, lastDirPath, "Script file (*.py)") if fileList: for pyFile in fileList: for i in range(self.tabEditorWidget.count()): tabWidget = self.tabEditorWidget.widget(i) if tabWidget.path == pyFile: self.tabEditorWidget.setCurrentWidget(tabWidget) break else: tabName = QFileInfo(pyFile).fileName() self.tabEditorWidget.newTabEditor(tabName, pyFile) lastDirPath = QFileInfo(pyFile).path() self.settings.setValue("pythonConsole/lastDirPath", pyFile) self.updateTabListScript(pyFile, action='append') def saveScriptFile(self): tabWidget = self.tabEditorWidget.currentWidget() try: tabWidget.save() except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) def saveAsScriptFile(self, index=None): tabWidget = self.tabEditorWidget.currentWidget() if not index: index = self.tabEditorWidget.currentIndex() if not tabWidget.path: fileName = self.tabEditorWidget.tabText(index) + '.py' folder = self.settings.value("pythonConsole/lastDirPath", QDir.home()) pathFileName = os.path.join(folder, fileName) fileNone = True else: pathFileName = tabWidget.path fileNone = False saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As") filename, filter = QFileDialog.getSaveFileName(self, saveAsFileTr, pathFileName, "Script file (*.py)") if filename: try: tabWidget.save(filename) except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) if fileNone: tabWidget.path = None else: tabWidget.path = pathFileName return if not fileNone: self.updateTabListScript(pathFileName, action='remove') def openHelp(self): QgsContextHelp.run("PythonConsole") def openSettings(self): if optionsDialog(self).exec_(): self.shell.refreshSettingsShell() self.shellOut.refreshSettingsOutput() self.tabEditorWidget.refreshSettingsEditor() def callWidgetMessageBar(self, text): self.shellOut.widgetMessageBar(iface, text) def callWidgetMessageBarEditor(self, text, level, timed): self.tabEditorWidget.widgetMessageBar(iface, text, level, timed) def updateTabListScript(self, script, action=None): if action == 'remove': self.tabListScript.remove(script) elif action == 'append': if not self.tabListScript: self.tabListScript = [] if script not in self.tabListScript: self.tabListScript.append(script) else: self.tabListScript = [] self.settings.setValue("pythonConsole/tabScripts", self.tabListScript) def saveSettingsConsole(self): self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState()) self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState()) self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState()) self.shell.writeHistoryFile(True) def restoreSettingsConsole(self): storedTabScripts = self.settings.value("pythonConsole/tabScripts", []) self.tabListScript = storedTabScripts self.splitter.restoreState(self.settings.value("pythonConsole/splitterConsole", QByteArray())) self.splitterEditor.restoreState(self.settings.value("pythonConsole/splitterEditor", QByteArray())) self.splitterObj.restoreState(self.settings.value("pythonConsole/splitterObj", QByteArray()))
class CoordinateCaptureDockWidget(QDockWidget): closingPlugin = pyqtSignal() def __init__(self, parent=None): """Constructor.""" super(CoordinateCaptureDockWidget, self).__init__(parent) self.setWindowTitle(self.tr("Coordinate Capture")) self.setGeometry(0, 0, 300, 228) self.dockWidgetContents = QWidget(self) self.setWidget(self.dockWidgetContents) self.gridLayout = QGridLayout() self.dockWidgetContents.setLayout(self.gridLayout) self.dockWidgetContents.layout().setColumnMinimumWidth(0, 36) self.userCrsToolButton = QToolButton(self.dockWidgetContents) self.userCrsToolButton.setIcon( QIcon(":/plugins/coordinate_capture/mIconProjectionEnabled.svg")) self.userCrsToolButton.setToolTip( self.tr("Click to select the CRS to use for coordinate display")) self.userCrsLabel = QLabel(self.dockWidgetContents) self.userCrsLabel.setPixmap( QPixmap(":/plugins/coordinate_capture/transformed.svg")) self.userCrsLabel.setGeometry(self.userCrsToolButton.geometry()) self.userCrsEdit = QLineEdit(self.dockWidgetContents) self.userCrsEdit.setReadOnly(True) self.userCrsEdit.setToolTip( self.tr("Coordinate in your selected CRS (lat,lon or east,north)")) self.copyUserCrsCoordinatesAction = self.userCrsEdit.addAction( QIcon(":/plugins/coordinate_capture/mActionEditCopy.svg"), QLineEdit.TrailingPosition) self.copyUserCrsCoordinatesAction.triggered.connect( self.copyUserCrsCoordinates) self.canvasCrsEdit = QLineEdit(self.dockWidgetContents) self.canvasCrsEdit.setReadOnly(True) self.canvasCrsEdit.setToolTip( self. tr("Coordinate in map canvas coordinate reference system (lat,lon or east,north)" )) self.copyCanvasCrsCoordinatesAction = self.canvasCrsEdit.addAction( QIcon(":/plugins/coordinate_capture/mActionEditCopy.svg"), QLineEdit.TrailingPosition) self.copyCanvasCrsCoordinatesAction.triggered.connect( self.copyCanvasCrsCoordinates) self.trackMouseButton = QToolButton(self.dockWidgetContents) self.trackMouseButton.setIcon( QIcon(":/plugins/coordinate_capture/tracking.svg")) self.trackMouseButton.setCheckable(True) self.trackMouseButton.setToolTip( self.tr( "Click to enable mouse tracking. Click the canvas to stop")) self.trackMouseButton.setChecked(False) # Create the action for tool self.captureButton = QPushButton(self.dockWidgetContents) self.captureButton.setText(self.tr("Start Capture")) self.captureButton.setToolTip( self.tr("Click to enable coordinate capture")) self.captureButton.setIcon( QIcon(":/plugins/coordinate_capture/coordinate_capture.png")) self.captureButton.setWhatsThis( self. tr("Click on the map to view coordinates and capture to clipboard." )) # // Set the icons # setCurrentTheme(QString()); self.dockWidgetContents.layout().addWidget(self.userCrsToolButton, 0, 0) self.dockWidgetContents.layout().addWidget(self.userCrsEdit, 0, 1) self.dockWidgetContents.layout().addWidget(self.userCrsLabel, 1, 0) self.dockWidgetContents.layout().addWidget(self.canvasCrsEdit, 1, 1) self.dockWidgetContents.layout().addWidget(self.trackMouseButton, 2, 0) self.dockWidgetContents.layout().addWidget(self.captureButton, 2, 1) def copyUserCrsCoordinates(self): self.userCrsEdit.selectAll() self.userCrsEdit.copy() def copyCanvasCrsCoordinates(self): self.canvasCrsEdit.selectAll() self.canvasCrsEdit.copy() def closeEvent(self, event): self.closingPlugin.emit() event.accept()
class ForeignKeyMapper(QWidget): """ Widget for selecting database records through an entity browser or using an ExpressionBuilder for filtering records. """ # Custom signals beforeEntityAdded = pyqtSignal("PyQt_PyObject") afterEntityAdded = pyqtSignal("PyQt_PyObject", int) entityRemoved = pyqtSignal("PyQt_PyObject") deletedRows = pyqtSignal(list) def __init__(self, entity, parent=None, notification_bar=None, enable_list=True, can_filter=False, plugin=None): QWidget.__init__(self, parent) self.current_profile = current_profile() self._tbFKEntity = QTableView(self) self._tbFKEntity.setEditTriggers(QAbstractItemView.NoEditTriggers) self._tbFKEntity.setAlternatingRowColors(True) self._tbFKEntity.setSelectionBehavior(QAbstractItemView.SelectRows) self.plugin = plugin self._add_entity_btn = QToolButton(self) self._add_entity_btn.setToolTip( QApplication.translate("ForeignKeyMapper", "Add")) self._add_entity_btn.setIcon(GuiUtils.get_icon("add.png")) self._add_entity_btn.clicked.connect(self.onAddEntity) self._edit_entity_btn = QToolButton(self) self._edit_entity_btn.setVisible(False) self._edit_entity_btn.setToolTip( QApplication.translate("ForeignKeyMapper", "Edit")) self._edit_entity_btn.setIcon(GuiUtils.get_icon("edit.png")) self._filter_entity_btn = QToolButton(self) self._filter_entity_btn.setVisible(False) self._filter_entity_btn.setToolTip( QApplication.translate("ForeignKeyMapper", "Select by expression")) self._filter_entity_btn.setIcon(GuiUtils.get_icon("filter.png")) self._filter_entity_btn.clicked.connect(self.onFilterEntity) self._delete_entity_btn = QToolButton(self) self._delete_entity_btn.setToolTip( QApplication.translate("ForeignKeyMapper", "Remove")) self._delete_entity_btn.setIcon(GuiUtils.get_icon("remove.png")) self._delete_entity_btn.clicked.connect(self.onRemoveEntity) layout = QVBoxLayout(self) layout.setSpacing(4) layout.setMargin(5) self.grid_layout = QGridLayout() self.grid_layout.setHorizontalSpacing(5) self.grid_layout.addWidget(self._add_entity_btn, 0, 0, 1, 1) self.grid_layout.addWidget(self._filter_entity_btn, 0, 1, 1, 1) self.grid_layout.addWidget(self._edit_entity_btn, 0, 2, 1, 1) self.grid_layout.addWidget(self._delete_entity_btn, 0, 3, 1, 1) self.grid_layout.setColumnStretch(4, 5) layout.addLayout(self.grid_layout) layout.addWidget(self._tbFKEntity) self.social_tenure = self.current_profile.social_tenure self._tableModel = None self._notifBar = notification_bar self._headers = [] self._entity_attrs = [] self._cell_formatters = {} self._searchable_columns = OrderedDict() self._supportsLists = enable_list self._deleteOnRemove = False self._uniqueValueColIndices = OrderedDict() self.global_id = None self._deferred_objects = {} self._use_expression_builder = can_filter if self._use_expression_builder: self._filter_entity_btn.setVisible(True) self._edit_entity_btn.setVisible(False) self.set_entity(entity) def set_entity(self, entity): """ Sets new entity and updates the ForeignKeyMapper with a new :param entity: The entity of the ForeignKeyMapper :type entity:Object """ from stdm.ui.entity_browser import EntityBrowser self._entity = entity self._dbmodel = entity_model(entity) self._init_fk_columns() self._entitySelector = EntityBrowser(self._entity, parent=self, state=SELECT, plugin=self.plugin) # Connect signals self._entitySelector.recordSelected.connect( self._onRecordSelectedEntityBrowser) def _init_fk_columns(self): """ Asserts if the entity columns actually do exist in the database. The method also initializes the table headers, entity column and cell formatters. """ self._headers[:] = [] self._entity_attrs[:] = [] if self._dbmodel is None: msg = QApplication.translate( 'ForeignKeyMapper', 'The data model for ' 'the entity could ' 'not be loaded, ' 'please contact ' 'your database ' 'administrator.') QMessageBox.critical( self, QApplication.translate('EntityBrowser', 'Entity Browser'), msg) return table_name = self._entity.name columns = table_column_names(table_name) missing_columns = [] header_idx = 0 # Iterate entity column and assert if they exist for c in self._entity.columns.values(): # Do not include virtual columns in list of missing columns if not c.name in columns and not isinstance(c, VirtualColumn): missing_columns.append(c.name) else: header = c.header() self._headers.append(header) ''' If it is a virtual column then use column name as the header but fully qualified column name (created by SQLAlchemy relationship) as the entity attribute name. ''' col_name = c.name if isinstance(c, MultipleSelectColumn): col_name = c.model_attribute_name self._entity_attrs.append(col_name) # Get widget factory so that we can use the value formatter w_factory = ColumnWidgetRegistry.factory(c.TYPE_INFO) if not w_factory is None: try: formatter = w_factory(c) self._cell_formatters[col_name] = formatter except WidgetException as we: msg = QApplication.translate( 'ForeignKeyMapper', 'Error in creating column:') msg = '{0} {1}:{2}\n{3}'.format( msg, self._entity.name, c.name, str(we)) QMessageBox.critical( self, QApplication.translate('ForeignKeyMapper', 'Widget Creation Error'), msg) # Set searchable columns if c.searchable: self._searchable_columns[header] = { 'name': c.name, 'header_index': header_idx } header_idx += 1 if len(missing_columns) > 0: msg = QApplication.translate( 'ForeignKeyMapper', 'The following columns have been defined in the ' 'configuration but are missing in corresponding ' 'database table, please re-run the configuration wizard ' 'to create them.\n{0}'.format('\n'.join(missing_columns))) QMessageBox.warning( self, QApplication.translate('ForeignKeyMapper', 'Entity Browser'), msg) self._tableModel = BaseSTDMTableModel([], self._headers, self) self._tbFKEntity.setModel(self._tableModel) # First (id) column will always be hidden self._tbFKEntity.hideColumn(0) self._tbFKEntity.horizontalHeader().setSectionResizeMode( QHeaderView.Interactive) self._tbFKEntity.horizontalHeader().setStretchLastSection(True) self._tbFKEntity.verticalHeader().setVisible(True) def databaseModel(self): ''' Returns the database model that represents the foreign key entity. ''' return self._dbmodel def setDatabaseModel(self, model): ''' Set the database model that represents the foreign key entity. Model has to be a callable. ''' self._dbmodel = model def cellFormatters(self): """ Returns a dictionary of cell formatters used by the foreign key mapper. """ return self._cell_formatters def cell_formatter(self, column): """ :param column: Column name: :type column: str :return: Returns the corresponding formatter object based on the column name. None will be returned if there is no corresponding object. :rtype: object """ return self._cell_formatters.get(column, None) def entitySelector(self): ''' Returns the dialog for selecting the entity objects. ''' return self._entitySelector def supportList(self): ''' Returns whether the mapper supports only one item or multiple entities. Default is 'True'. ''' return self._supportsLists def setSupportsList(self, supportsList): ''' Sets whether the mapper supports only one item or multiple entities i.e. one-to-one (False) and one-to-many mapping (True). ''' self._supportsLists = supportsList def setNotificationBar(self, notificationBar): ''' Set the notification bar for displaying user messages. ''' self._notifBar = notificationBar def viewModel(self): ''' Return the view model used by the table view. ''' return self._tableModel def set_expression_builder(self, state): """ Set the mapper to use QGIS expression builder as the entity selector. """ self._use_expression_builder = state def expression_builder_enabled(self): """ Returns whether the mapper has been configured to use the expression builder """ return self._use_expression_builder def deleteOnRemove(self): ''' Returns the state whether a record should be deleted from the database when it is removed from the list. ''' return self._deleteOnRemove def setDeleteonRemove(self, delete): ''' Set whether whether a record should be deleted from the database when it is removed from the list. ''' self._deleteOnRemove = delete def addUniqueColumnName(self, colName, replace=True): ''' Set the name of the column whose values are to be unique. If 'replace' is True then the existing row will be replaced with one with the new value; else, the new row will not be added to the list. ''' headers = list(self._dbmodel.displayMapping().values()) colIndex = getIndex(headers, colName) if colIndex != -1: self.addUniqueColumnIndex(colIndex, replace) def addUniqueColumnIndex(self, colIndex, replace=True): ''' Set the index of the column whose values are to be unique. The column indices are zero-based. If 'replace' is True then the existing row will be replaced with the new value; else, the new row will not be added to the list. For multiple replace rules defined, then the first one added to the collection is the one that will be applied. ''' self._uniqueValueColIndices[colIndex] = replace def onFilterEntity(self): """ Slot raised to load the expression builder dialog. """ vl, msg = self.vector_layer() if vl is None: msg = msg + "\n" + QApplication.translate( "ForeignKeyMapper", "The expression builder cannot be used at this moment.") QMessageBox.critical( self, QApplication.translate("ForeignKeyMapper", "Expression Builder"), msg) return context = self._entity.short_name filter_dlg = ForeignKeyMapperExpressionDialog(vl, self, context=context) filter_dlg.setWindowTitle( QApplication.translate("ForeignKeyMapper", "Filter By Expression")) filter_dlg.recordSelected[int].connect( self._onRecordSelectedEntityBrowser) res = filter_dlg.exec_() def _removeRow(self, rowNumber): ''' Remove the row at the given index. ''' self._tableModel.removeRows(rowNumber, 1) def onRemoveEntity(self): ''' Slot raised on clicking to remove the selected entity. ''' selectedRowIndices = self._tbFKEntity.selectionModel().selectedRows(0) deleted_rows = [] if len(selectedRowIndices) == 0: msg = QApplication.translate( "ForeignKeyMapper", "Please select the record to be removed.") self._notifBar.clear() self._notifBar.insertWarningNotification(msg) return for selectedRowIndex in selectedRowIndices: # Delete record from database if flag has been set to True recId = selectedRowIndex.data() dbHandler = self._dbmodel() delRec = dbHandler.queryObject().filter( self._dbmodel.id == recId).first() if not delRec is None: self.entityRemoved.emit(delRec) if self._deleteOnRemove: delRec.delete() self._removeRow(selectedRowIndex.row()) deleted_rows.append(selectedRowIndex.row()) self.deletedRows.emit(deleted_rows) def _recordIds(self): ''' Returns the primary keys of the records in the table. ''' recordIds = [] if self._tableModel: rowCount = self._tableModel.rowCount() for r in range(rowCount): # Get ID value modelIndex = self._tableModel.index(r, 0) modelId = modelIndex.data() recordIds.append(modelId) return recordIds def entities(self): ''' Returns the model instance(s) depending on the configuration specified by the user. ''' recIds = self._recordIds() modelInstances = self._modelInstanceFromIds(recIds) if len(modelInstances) == 0: if self._supportsLists: return [] else: return None else: if self._supportsLists: return modelInstances else: return modelInstances[0] def setEntities(self, entities): ''' Insert entities into the table. ''' if isinstance(entities, list): for entity in entities: self._insertModelToView(entity) else: self._insertModelToView(entities) def searchModel(self, columnIndex, columnValue): ''' Searches for 'columnValue' in the column whose index is specified by 'columnIndex' in all rows contained in the model. ''' if isinstance(columnValue, QVariant): columnValue = str(columnValue.toString()) if not isinstance(columnValue, str): columnValue = str(columnValue) columnValue = columnValue.strip() proxy = QSortFilterProxyModel(self) proxy.setSourceModel(self._tableModel) proxy.setFilterKeyColumn(columnIndex) proxy.setFilterFixedString(columnValue) # Will return model index containing the primary key. matchingIndex = proxy.mapToSource(proxy.index(0, 0)) return matchingIndex def _modelInstanceFromIds(self, ids): ''' Returns the model instance based the value of its primary key. ''' dbHandler = self._dbmodel() modelInstances = [] for modelId in ids: modelObj = dbHandler.queryObject().filter( self._dbmodel.id == modelId).first() if not modelObj is None: modelInstances.append(modelObj) return modelInstances def _onRecordSelectedEntityBrowser(self, rec, row_number=-1): ''' Slot raised when the user has clicked the select button in the 'EntityBrowser' dialog to add the selected record to the table's list. Add the record to the foreign key table using the mappings. ''' # Check if the record exists using the primary key so as to ensure # only one instance is added to the table if isinstance(rec, int): recIndex = getIndex(self._recordIds(), rec) if recIndex != -1: return dbHandler = self._dbmodel() modelObj = dbHandler.queryObject().filter( self._dbmodel.id == rec).first() elif isinstance(rec, object): modelObj = rec else: return if modelObj is not None: # Raise before entity added signal self.beforeEntityAdded.emit(modelObj) # Validate for unique value configurations ''' if not self._validate_unique_columns(modelObj, row_number): return ''' if not self._supportsLists and self._tableModel.rowCount() > 0: self._removeRow(0) insert_position = self._insertModelToView(modelObj, row_number) if isinstance(rec, object): self._deferred_objects[insert_position] = modelObj def _validate_unique_columns(self, model, exclude_row=-1): """ Loop through the attributes of the model to assert for existing row values that should be unique based on the configuration of unique columns. """ for colIndex, replace in list(self._uniqueValueColIndices.items()): attrName = list(self._dbmodel.displayMapping().keys())[colIndex] attrValue = getattr(model, attrName) # Check to see if there are cell formatters so # that the correct value is searched for in the model if attrName in self._cell_formatters: attrValue = self._cell_formatters[attrName](attrValue) matchingIndex = self.searchModel(colIndex, attrValue) if matchingIndex.isValid() and matchingIndex.row() != exclude_row: if replace: existingId = matchingIndex.data() # Delete old record from db entityModels = self._modelInstanceFromIds([existingId]) if len(entityModels) > 0: entityModels[0].delete() self._removeRow(matchingIndex.row()) return True else: # Break. Do not add item to the list. return False return True def _insertModelToView(self, model_obj, row_number=-1): """ Insert the given database model instance into the view at the given row number position. """ if row_number == -1: row_number = self._tableModel.rowCount() self._tableModel.insertRows(row_number, 1) # In some instances, we will need to get the model object with # backrefs included else exceptions will be raised on missing # attributes q_objs = self._modelInstanceFromIds([model_obj.id]) if len(q_objs) == 0: return model_obj = q_objs[0] for i, attr in enumerate(self._entity_attrs): prop_idx = self._tableModel.index(row_number, i) attr_val = getattr(model_obj, attr) ''' Check if there are display formatters and apply if one exists for the given attribute. ''' if attr in self._cell_formatters: formatter = self._cell_formatters[attr] attr_val = formatter.format_column_value(attr_val) self._tableModel.setData(prop_idx, attr_val) # Raise signal once entity has been inserted self.afterEntityAdded.emit(model_obj, row_number) self._tbFKEntity.resizeColumnsToContents() return row_number def insert_model_to_table(self, model_obj, row_number=-1): """ Insert the given database model instance into the view at the given row number position. """ if row_number == -1: row_number = self._tableModel.rowCount() self._tableModel.insertRows(row_number, 1) # In some instances, we will need to get the model object with # backrefs included else exceptions will be raised on missing # attributes q_objs = self._modelInstanceFromIds([model_obj.id]) if len(q_objs) == 0: return model_obj = q_objs[0] for i, attr in enumerate(self._entity_attrs): prop_idx = self._tableModel.index(row_number, i) attr_val = getattr(model_obj, attr) # Check if there are display formatters and apply if one exists # for the given attribute. if attr in self._cell_formatters: formatter = self._cell_formatters[attr] attr_val = formatter.format_column_value(attr_val) self._tableModel.setData(prop_idx, attr_val) #self._tbFKEntity.resizeColumnsToContents() self._tbFKEntity.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) return row_number def remove_rows(self): """ Removes rows from the fk browser. """ row_count = self._tbFKEntity.model().rowCount() self._tbFKEntity.model().removeRows(0, row_count) def vector_layer(self): """ Returns a QgsVectorLayer based on the configuration information specified in the mapper including the system-wide data connection properties. """ from stdm.data.pg_utils import vector_layer if self._dbmodel is None: msg = QApplication.translate( "ForeignKeyMapper", "Primary database model object not defined.") return None, msg filter_layer = vector_layer(self._entity.name) if filter_layer is None: msg = QApplication.translate( "ForeignKeyMapper", "Vector layer could not be constructed from the database table." ) return None, msg if not filter_layer.isValid(): trans_msg = QApplication.translate( "ForeignKeyMapper", "The vector layer for '{0}' table is invalid.") msg = trans_msg.format(self._entity.name) return None, msg return filter_layer, "" def onAddEntity(self): """ Slot raised on selecting to add related entities that will be mapped to the primary database model instance. """ self._entitySelector.buttonBox.button( QDialogButtonBox.Cancel).setVisible(False) # Clear any previous selections in the entity browser self._entitySelector.clear_selection() # Clear any previous notifications self._entitySelector.clear_notifications() self._entitySelector.exec_()
def fill_tree_with_element(widget, treeItem, elt, ns_imap={}, custom_viewers={}, ns_map={}): """ :param widget: the QTreeWidget :param treeItem: a QTreeWidgetItem to fill :param elt: the XML node :param ns_imap: an "inverse" namespace map { uri : prefix } :param custom_viewers: a dict giving a custom viewer plugin (QWidget) for some elements {tag : constructor} :param ns_map: a namespace map { prefix : uri } """ is_root = treeItem == widget.invisibleRootItem() # tag ns, tag = split_tag(elt.tag) if ns and ns_imap.get(ns): treeItem.setText(0, ns_imap[ns] + ":" + tag) else: treeItem.setText(0, tag) f = treeItem.font(0) f.setBold(True) treeItem.setFont(0, f) # custom viewer if elt.tag in custom_viewers: custom_viewer_widget, filter = custom_viewers[elt.tag] if filter is None or elt.find(filter, ns_map) is not None: btn = QToolButton(widget) btn.setIcon(custom_viewer_widget.icon()) btn.setIconSize(QSize(32, 32)) def show_viewer(btn): widget.w = custom_viewer_widget.init_from_xml(elt) widget.w.setWindowModality(Qt.WindowModal) widget.w.show() btn.clicked.connect(show_viewer) w = QWidget(widget) l = QHBoxLayout() l.addWidget(btn) l.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding)) w.setLayout(l) if is_root: # insert an item child = QTreeWidgetItem() treeItem.addChild(child) widget.setItemWidget(child, 0, w) else: widget.setItemWidget(treeItem, 1, w) # attributes for k, v in elt.attrib.items(): child = QTreeWidgetItem() treeItem.addChild(child) if "}" in k: i = k.index("}") ns = k[1:i] # get ns prefix from ns uri p = ns_imap.get(ns) if p is not None: n = p + ":" + k[i + 1:] else: n = k[i + 1:] else: n = no_prefix(k) child.setText(0, "@" + n) if n == "xlink:href" and v.startswith("http"): html = QLabel(widget) html.setOpenExternalLinks(True) html.setTextFormat(Qt.RichText) html.setText('<a href="{}">{}</a>'.format(v, v)) child.setData(1, Qt.UserRole, v) widget.setItemWidget(child, 1, html) else: child.setText(1, v) # text if elt.text: treeItem.setText(1, elt.text) # children for xmlChild in elt: child = QTreeWidgetItem() treeItem.addChild(child) fill_tree_with_element(widget, child, xmlChild, ns_imap, custom_viewers, ns_map)
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameters: if param.isAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr(' (x, y)') try: if param.optional: desc += self.tr(' [optional]') except: pass widget = self.getWidgetFromParameter(param) self.valueItems[param.name] = widget if isinstance(param, ParameterVector) and \ not self.alg.allowOnlyOpenedLayers: layout = QHBoxLayout() layout.setSpacing(2) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setToolTip(self.tr('Iterate over this layer')) button.setCheckable(True) layout.addWidget(button) self.iterateButtons[param.name] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) tooltips = self.alg.getParameterDescriptions() widget.setToolTip(tooltips.get(param.name, param.description)) if isinstance(param, ParameterBoolean): widget.setText(desc) if param.isAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) else: label = QLabel(desc) #label.setToolTip(tooltip) self.labels[param.name] = label if param.isAdvanced: self.layoutAdvanced.addWidget(label) self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) self.widgets[param.name] = widget for output in self.alg.outputs: if output.hidden: continue label = QLabel(output.description) widget = OutputSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (OutputRaster, OutputVector, OutputTable)): check = QCheckBox() check.setText(self.tr('Open output file after running algorithm')) check.setChecked(True) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name] = check self.valueItems[output.name] = widget if isinstance(output, OutputVector): if output.base_input in self.dependentItems: items = self.dependentItems[output.base_input] else: items = [] self.dependentItems[output.base_input] = items items.append(output) base_input = self.alg.getParameterFromName(output.base_input) if isinstance(base_input, ParameterVector): layers = dataobjects.getVectorLayers(base_input.datatype) else: layers = dataobjects.getTables() if len(layers) > 0: output.base_layer = layers[0]
class PlanetExplorer(object): def __init__(self, iface): self.iface = iface # Initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Initialize locale locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join( self.plugin_dir, "i18n", "{0}Plugin_{1}.qm".format(PE, locale) ) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr("&{0}".format(P_E)) self.toolbar = None # noinspection PyTypeChecker self.explorer_dock_widget = None self._terms_browser = None if is_segments_write_key_valid(): analytics.write_key = segments_write_key() if is_sentry_dsn_valid(): try: sentry_sdk.init(sentry_dsn(), release=plugin_version(True)) sentry_sdk.set_context( "qgis", { "type": "runtime", "name": Qgis.QGIS_RELEASE_NAME, "version": Qgis.QGIS_VERSION, }, ) system = platform.system() if system == "Darwin": sentry_sdk.set_context( "mac", { "type": "os", "name": "macOS", "version": platform.mac_ver()[0], "kernel_version": platform.uname().release, }, ) if system == "Linux": sentry_sdk.set_context( "linux", { "type": "os", "name": "Linux", "version": platform.release(), "build": platform.version(), }, ) if system == "Windows": sentry_sdk.set_context( "windows", { "type": "os", "name": "Windows", "version": platform.version(), }, ) except Exception: QMessageBox.warning( self.iface.mainWindow(), "Error", "Error initializing Planet Explorer.\n" "Please restart QGIS to load updated libraries.", ) self.qgis_hook = sys.excepthook def plugin_hook(t, value, tb): trace = "".join(traceback.format_exception(t, value, tb)) if PLUGIN_NAMESPACE in trace.lower(): s = "" if issubclass(t, exceptions.Timeout): s = "Connection to Planet server timed out." elif issubclass(t, exceptions.ConnectionError): s = ( "Connection error.\n Verify that your computer is correctly" " connected to the Internet" ) elif issubclass(t, (exceptions.ProxyError, exceptions.InvalidProxyURL)): s = ( "ProxyError.\n Verify that your proxy is correctly configured" " in the QGIS settings" ) elif issubclass(t, planet.api.exceptions.ServerError): s = "Server Error.\n Please, try again later" elif issubclass(t, urllib3.exceptions.ProxySchemeUnknown): s = ( "Proxy Error\n Proxy URL must start with 'http://' or" " 'https://'" ) if s: QMessageBox.warning(self.iface.mainWindow(), "Error", s) else: try: sentry_sdk.capture_exception(value) except Exception: pass # we swallow all exceptions here, to avoid entering an endless loop self.qgis_hook(t, value, tb) else: self.qgis_hook(t, value, tb) sys.excepthook = plugin_hook def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(PE, message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None, ): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToWebMenu(self.menu, action) self.actions.append(action) return action # noinspection PyPep8Naming def initGui(self): self.toolbar = self.iface.addToolBar(P_E) self.toolbar.setObjectName(P_E) self.showdailyimages_act = self.add_action( os.path.join(plugin_path, "resources", "search.svg"), text=self.tr(P_E), callback=toggle_images_search, add_to_menu=True, add_to_toolbar=True, parent=self.iface.mainWindow(), ) self.showbasemaps_act = self.add_action( os.path.join(plugin_path, "resources", "basemap.svg"), text=self.tr("Show Basemaps Search"), callback=toggle_mosaics_search, add_to_menu=True, add_to_toolbar=True, parent=self.iface.mainWindow(), ) self.showinspector_act = self.add_action( os.path.join(plugin_path, "resources", "inspector.svg"), text=self.tr("Show Planet Inspector..."), callback=toggle_inspector, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow(), ) self.showtasking_act = self.add_action( os.path.join(plugin_path, "resources", "tasking.svg"), text=self.tr("Show Tasking..."), callback=toggle_tasking_widget, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow(), ) self.add_central_toolbar_button() self.showorders_act = self.add_action( os.path.join(plugin_path, "resources", "orders.svg"), text=self.tr("Show Orders Monitor..."), callback=toggle_orders_monitor, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow(), ) self.add_user_button() self.add_info_button() self.settings_act = self.add_action( os.path.join(plugin_path, "resources", "cog.svg"), text=self.tr("Settings..."), callback=self.show_settings, add_to_menu=True, add_to_toolbar=False, parent=self.iface.mainWindow(), ) self.provider = BasemapLayerWidgetProvider() QgsGui.layerTreeEmbeddedWidgetRegistry().addProvider(self.provider) QgsProject.instance().projectSaved.connect(self.project_saved) QgsProject.instance().layersAdded.connect(self.layers_added) QgsProject.instance().layerRemoved.connect(self.layer_removed) PlanetClient.getInstance().loginChanged.connect(self.login_changed) self.enable_buttons(False) def add_central_toolbar_button(self): widget = QWidget() widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) layout = QHBoxLayout() layout.addStretch() self.btnLogin = QPushButton() palette = self.btnLogin.palette() palette.setColor(QPalette.Button, PLANET_COLOR) self.btnLogin.setPalette(palette) self.btnLogin.setText("Log in") # self.btnLogin.setAutoRaise(True) self.btnLogin.setAttribute(Qt.WA_TranslucentBackground) self.btnLogin.clicked.connect(self.btn_login_clicked) icon = QIcon(os.path.join(plugin_path, "resources", "planet-logo-p.svg")) labelIcon = QLabel() labelIcon.setPixmap(icon.pixmap(QSize(16, 16))) layout.addWidget(labelIcon) self.labelLoggedIn = QLabel() self.labelLoggedIn.setText("") layout.addWidget(self.labelLoggedIn) layout.addWidget(self.btnLogin) layout.addStretch() widget.setLayout(layout) self.toolbar.addWidget(widget) def btn_login_clicked(self): if PlanetClient.getInstance().has_api_key(): self.logout() else: self.login() def layer_removed(self, layer): self.provider.layerWasRemoved(layer) def layers_added(self, layers): for layer in layers: add_widget_to_layer(layer) def login_changed(self, loggedin): self.provider.updateLayerWidgets() self.enable_buttons(loggedin) if not loggedin: hide_orders_monitor() hide_inspector() def add_info_button(self): info_menu = QMenu() add_menu_section_action("Planet", info_menu) p_com_act = QAction(QIcon(EXT_LINK), "planet.com", info_menu) p_com_act.triggered[bool].connect(lambda: open_link_with_browser(PLANET_COM)) info_menu.addAction(p_com_act) p_explorer_act = QAction(QIcon(EXT_LINK), "Planet Explorer web app", info_menu) p_explorer_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_EXPLORER) ) info_menu.addAction(p_explorer_act) p_sat_act = QAction(QIcon(EXT_LINK), "Satellite specs PDF", info_menu) p_sat_act.triggered[bool].connect(lambda: open_link_with_browser(SAT_SPECS_PDF)) info_menu.addAction(p_sat_act) p_support_act = QAction(QIcon(EXT_LINK), "Support Community", info_menu) p_support_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SUPPORT_COMMUNITY) ) info_menu.addAction(p_support_act) p_whatsnew_act = QAction(QIcon(EXT_LINK), "What's new", info_menu) p_whatsnew_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_INTEGRATIONS) ) info_menu.addAction(p_whatsnew_act) p_sales_act = QAction(QIcon(EXT_LINK), "Sales", info_menu) p_sales_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SALES) ) info_menu.addAction(p_sales_act) add_menu_section_action("Documentation", info_menu) terms_act = QAction("Terms", info_menu) terms_act.triggered[bool].connect(self.show_terms) info_menu.addAction(terms_act) btn = QToolButton() btn.setIcon( QIcon( os.path.join(plugin_path, "resources", "info.svg"), ) ) btn.setMenu(info_menu) btn.setPopupMode(QToolButton.MenuButtonPopup) # Also show menu on click, to keep disclosure triangle visible btn.clicked.connect(btn.showMenu) self.toolbar.addWidget(btn) def add_user_button(self): user_menu = QMenu() self.acct_act = QAction(QIcon(EXT_LINK), "Account", user_menu) self.acct_act.triggered[bool].connect( lambda: QDesktopServices.openUrl(QUrl(ACCOUNT_URL)) ) user_menu.addAction(self.acct_act) self.logout_act = QAction("Logout", user_menu) self.logout_act.triggered[bool].connect(self.logout) user_menu.addAction(self.logout_act) self.user_button = QToolButton() self.user_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.user_button.setIcon( QIcon( os.path.join(plugin_path, "resources", "account.svg"), ) ) self.user_button.setMenu(user_menu) self.user_button.setPopupMode(QToolButton.MenuButtonPopup) # Also show menu on click, to keep disclosure triangle visible self.user_button.clicked.connect(self.user_button.showMenu) self.toolbar.addWidget(self.user_button) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" PlanetClient.getInstance().log_out() self.provider.updateLayerWidgets() for action in self.actions: self.iface.removePluginWebMenu(self.tr("&{0}".format(P_E)), action) self.iface.removeToolBarIcon(action) # remove the toolbar if self.toolbar is not None: del self.toolbar remove_inspector() remove_explorer() remove_orders_monitor() remove_tasking_widget() QgsGui.layerTreeEmbeddedWidgetRegistry().removeProvider(self.provider.id()) sys.excepthook = self.qgis_hook QgsProject.instance().projectSaved.disconnect(self.project_saved) QgsProject.instance().layersAdded.disconnect(self.layers_added) QgsProject.instance().layerRemoved.disconnect(self.layer_removed) # ----------------------------------------------------------- def show_settings(self): dlg = SettingsDialog() dlg.exec() def show_terms(self, _): if self._terms_browser is None: self._terms_browser = QTextBrowser() self._terms_browser.setReadOnly(True) self._terms_browser.setOpenExternalLinks(True) self._terms_browser.setMinimumSize(600, 700) # TODO: Template terms.html first section, per subscription level # Collect subscription info from self.p_client.user self._terms_browser.setSource( QUrl("qrc:/plugins/planet_explorer/terms.html") ) self._terms_browser.setWindowModality(Qt.ApplicationModal) self._terms_browser.show() def login(self): if Qgis.QGIS_VERSION_INT >= 32000 and platform.system() == "Darwin": text = ( "WARNING: Your configuration may encounter serious issues with the" " Planet QGIS Plugin using QGIS V3.20. We are actively troubleshooting" " the issue with the QGIS team, you can track <a" " href='https://github.com/qgis/QGIS/issues/44182'>Issue 44182" " here</a>. In the meantime, we recommend that you use a QGIS version" " between 3.10 and 3.20, such as the 3.16 long term stable release. For" " further information including instructions on how to downgrade QGIS," " please refer to our <a" " href='https://support.planet.com/hc/en-us/articles/4404372169233'>support" " page here</a>." ) QMessageBox.warning(self.iface.mainWindow(), "Planet Explorer", text) show_explorer() def logout(self): PlanetClient.getInstance().log_out() def enable_buttons(self, loggedin): self.btnLogin.setVisible(not loggedin) labelText = "<b>Welcome to Planet</b>" if not loggedin else "<b>Planet</b>" self.labelLoggedIn.setText(labelText) self.showdailyimages_act.setEnabled(loggedin) self.showbasemaps_act.setEnabled(loggedin) self.showinspector_act.setEnabled(loggedin) self.showorders_act.setEnabled(loggedin) self.showtasking_act.setEnabled(loggedin) self.user_button.setEnabled(loggedin) self.user_button.setText( PlanetClient.getInstance().user()["user_name"] if loggedin else "" ) if loggedin: self.showdailyimages_act.setToolTip( "Show / Hide the Planet Imagery Search Panel" ) self.showbasemaps_act.setToolTip( "Show / Hide the Planet Basemaps Search Panel" ) self.showorders_act.setToolTip("Show / Hide the Order Status Panel") self.showinspector_act.setToolTip("Show / Hide the Planet Inspector Panel") self.showtasking_act.setToolTip("Show / Hide the Tasking Panel") else: self.showdailyimages_act.setToolTip("Login to access Imagery Search") self.showbasemaps_act.setToolTip("Login to access Basemaps Search") self.showorders_act.setToolTip("Login to access Order Status") self.showinspector_act.setToolTip("Login to access Planet Inspector") self.showtasking_act.setToolTip("Login to access Tasking Panel") def project_saved(self): if PlanetClient.getInstance().has_api_key(): def resave(): try: path = QgsProject.instance().absoluteFilePath() if path.lower().endswith(".qgs"): with open(path, encoding="utf-8") as f: s = f.read() with open(path, "w", encoding="utf-8") as f: f.write(s.replace(PlanetClient.getInstance().api_key(), "")) else: tmpfilename = path + ".temp" qgsfilename = ( os.path.splitext(os.path.basename(path))[0] + ".qgs" ) with zipfile.ZipFile(path, "r") as zin: with zipfile.ZipFile(tmpfilename, "w") as zout: zout.comment = zin.comment for item in zin.infolist(): if not item.filename.lower().endswith(".qgs"): zout.writestr(item, zin.read(item.filename)) else: s = zin.read(item.filename).decode("utf-8") s = s.replace( PlanetClient.getInstance().api_key(), "" ) qgsfilename = item.filename os.remove(path) os.rename(tmpfilename, path) with zipfile.ZipFile( path, mode="a", compression=zipfile.ZIP_DEFLATED ) as zf: zf.writestr(qgsfilename, s) except Exception: QMessageBox.warning( self.iface.mainWindow(), "Error saving project", "There was an error while removing API keys from QGIS project" " file.\nThe project that you have just saved might contain" " Planet API keys in plain text.", ) QTimer.singleShot(100, resave)
class ExpressionLineEdit(QLineEdit): def __init__(self, column, host=None, parent=None): # Use a different pixmap self._current_profile = current_profile() QLineEdit.__init__(self, parent) self.column = column self._entity = self.column.entity self.layer = self.create_layer() self.host = host # Configure load button self.btn_load = QToolButton(parent) self.btn_load.setCursor(Qt.PointingHandCursor) self.btn_load.setFocusPolicy(Qt.NoFocus) px = GuiUtils.get_icon_pixmap('expression.png') self.btn_load.setIcon(QIcon(px)) self.btn_load.setIconSize(px.size()) self.btn_load.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') frame_width = self.set_button_minimum_size(self.btn_load) # Ensure that text does not overlay button padding = self.btn_load.sizeHint().width() + frame_width + 1 self.setStyleSheet('padding-right: ' + str(padding * 2) + 'px;') # Set layout self.button_layout = QHBoxLayout(self) self.button_layout.addWidget(self.btn_load, 0, Qt.AlignRight) self.button_layout.setSpacing(0) self.button_layout.setMargin(5) # Readonly as text generated automatically self.setReadOnly(True) # Current model object self._current_item = None def create_layer(self): srid = None column = '' if self.entity.has_geometry_column(): geom_cols = [col.name for col in self.entity.columns.values() if col.TYPE_INFO == 'GEOMETRY'] column = geom_cols[0] geom_col_obj = self.entity.columns[column] if geom_col_obj.srid >= 100000: srid = geom_col_obj.srid layer = vector_layer(self.entity.name, geom_column=column, proj_wkt=srid) return layer def get_feature_value(self, model=None): self.layer.startEditing() feature = None request = QgsFeatureRequest() if model is None: model = self.host.model() request.setFilterFid(model.id) feature_itr = self.layer.getFeatures(request) for feat in feature_itr: feature = feat break exp = QgsExpression(self.column.expression) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.layer.fields()) if feature is not None: value = exp.evaluate(feature) return value else: return None def set_button_minimum_size(self, button): """ Sets the minimum button size. :param button: The button to be set. :type button: QToolButton :return: Returns the frame width of the button :rtype: Integer """ frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) msz = self.minimumSizeHint() self.setMinimumSize( max( msz.width(), button.sizeHint().height() + frame_width * 2 + 2 ), max( msz.height(), button.sizeHint().height() + frame_width * 2 + 2 ) ) return frame_width @property def entity(self): """ :return: Returns the entity object corresponding to this widget. :rtype: Entity """ return self._entity def clear_line_edit(self): """ Clears the text in the line edit. """ self.clear() def on_expression_triggered(self, model=None): """ Slot raised to load browser for selecting foreign key entities. To be implemented by subclasses. """ self.format_display(self.get_feature_value(model)) return self.get_feature_value() def format_display(self, value): """ Extract object values to show in the line edit based on the specified display columns. """ if value is not None: self.setText(str(value))
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.isDestination(): continue else: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr(' (x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(2) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setToolTip(self.tr('Iterate over this layer, creating a separate output for every feature in the layer')) button.setCheckable(True) layout.addWidget(button) layout.setAlignment(button, Qt.AlignTop) self.iterateButtons[param.name()] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) widget.setToolTip(param.toolTip()) if type(widget) is QCheckBox: # checkbox widget - so description is embedded in widget rather than a separate # label widget.setText(desc) else: label = QLabel(desc) # label.setToolTip(tooltip) self.labels[param.name()] = label if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue label = QLabel(output.description()) widget = DestinationSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): check = QCheckBox() check.setText(self.tr('Open output file after running algorithm')) check.setChecked(True) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name()] = check widget.setToolTip(param.toolTip()) self.outputWidgets[output.name()] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
def initGui(self): # Add GUI elements self.splash_screen = ui.SplashScreen(self) self.config_dialog = ui.ConfigDialog() self.tilesManager = tiles.TilesManager() self.toolbar = self.iface.addToolBar("Travel Time Platform Toolbar") # Logo button = QPushButton(resources.logo, "") button.setIconSize(QSize(112, 30)) button.setFlat(True) button.pressed.connect(self.show_splash) self.toolbar.addWidget(button) self.toolbar.addSeparator() # Show toolbox action self.action_show_toolbox = QAction(resources.icon_general, tr("Show the toolbox"), self.iface.mainWindow()) self.action_show_toolbox.triggered.connect(self.show_toolbox) self.toolbar.addAction(self.action_show_toolbox) self.iface.addPluginToMenu(u"&Travel Time Platform", self.action_show_toolbox) # Add tiles tiles_menu = QMenu() for key, tile in self.tilesManager.tiles.items(): action = QAction(tile["resource"], tile["label"], tiles_menu) action.triggered.connect( functools.partial(self.tilesManager.add_layer, key)) action.setEnabled(self.tilesManager.has_tiles) tiles_menu.addAction(action) if not self.tilesManager.has_tiles: action = QAction(tr("Request access to backgrounds"), tiles_menu) action.triggered.connect( lambda: self.tilesManager.request_access()) tiles_menu.addAction(action) tiles_button = QToolButton() tiles_button.setToolTip(tr("Add background")) tiles_button.setIcon(resources.icon_tiles) tiles_button.setMenu(tiles_menu) tiles_button.setPopupMode(QToolButton.InstantPopup) # self.action_show_toolbox.triggered.connect(self.show_toolbox) self.toolbar.addWidget(tiles_button) # self.iface.addPluginToMenu(u"&Travel Time Platform", self.action_show_toolbox) self.toolbar.addSeparator() # Show help actions self.action_show_help = QAction(resources.icon_help, tr("Help"), self.iface.mainWindow()) self.action_show_help.triggered.connect(self.show_splash) self.toolbar.addAction(self.action_show_help) self.iface.addPluginToMenu(u"&Travel Time Platform", self.action_show_help) # Show config actions self.action_show_config = QAction( resources.icon_config, tr("Configure Travel Time Platform plugin"), self.iface.mainWindow(), ) self.action_show_config.triggered.connect(self.show_config) self.toolbar.addAction(self.action_show_config) self.iface.addPluginToMenu(u"&Travel Time Platform", self.action_show_config) # Add the provider to the registry QgsApplication.processingRegistry().addProvider(self.provider) # Show splash screen if not QSettings().value( "travel_time_platform/spashscreen_dontshowagain", False, type=bool): self.show_splash()
def __init__(self, iface): locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(os.path.dirname(__file__), 'i18n', 'annotationManager_{}.qm'.format(locale)) self.translator = None if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.iface = iface self.iface.projectRead.connect(self.projectOpen) self.dock = QDockWidget(self.tr('Annotations')) self.manager = QWidget() toolbar = QToolBar() self.annotationList = QListWidget() self.annotationList.setSelectionMode( QAbstractItemView.ExtendedSelection) self.annotationList.itemSelectionChanged.connect(self.selectAnnotation) self.annotationList.itemChanged.connect(self.checkItem) action_refresh = QAction( QIcon(':/plugins/annotationManager/resources/mActionDraw.png'), self.tr('Refresh the annotations list'), self.manager) action_refresh.triggered.connect(self.refreshAnnotations) action_remove = QAction( QIcon( ':/plugins/annotationManager/resources/mActionRemoveAnnotation.png' ), self.tr('Remove the selected annotation'), self.manager) action_remove.triggered.connect(self.removeAnnotation) viewMenu = QMenu() action_showAll = QAction( QIcon(':/plugins/annotationManager/resources/mActionShowAll.png'), self.tr('Show all annotations'), self.manager) action_showAll.triggered.connect(self.showAll) action_hideAll = QAction( QIcon(':/plugins/annotationManager/resources/mActionHideAll.png'), self.tr('Hide all annotations'), self.manager) action_hideAll.triggered.connect(self.hideAll) action_showAllSelected = QAction( QIcon(':/plugins/annotationManager/resources/mActionShowAll.png'), self.tr('Show all selected annotations'), self.manager) action_showAllSelected.triggered.connect(self.showAllSelected) action_hideAllSelected = QAction( QIcon(':/plugins/annotationManager/resources/mActionHideAll.png'), self.tr('Hide all selected annotations'), self.manager) action_hideAllSelected.triggered.connect(self.hideAllSelected) viewMenu.addAction(action_showAll) viewMenu.addAction(action_hideAll) viewMenu.addAction(action_showAllSelected) viewMenu.addAction(action_hideAllSelected) viewButton = QToolButton() viewButton.setIcon( QIcon(':/plugins/annotationManager/resources/mActionShowAll.png')) viewButton.setPopupMode(2) viewButton.setMenu(viewMenu) toolbar.addAction(action_refresh) toolbar.addAction(action_remove) toolbar.addWidget(viewButton) toolbar.setIconSize(QSize(16, 16)) p1_vertical = QVBoxLayout() p1_vertical.setContentsMargins(0, 0, 0, 0) p1_vertical.addWidget(toolbar) p1_vertical.addWidget(self.annotationList) self.manager.setLayout(p1_vertical) self.dock.setWidget(self.manager) self.dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) self.rb = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self.project = QgsProject.instance() self.annotationManager = self.project.annotationManager() self.annotationManager.annotationAdded.connect(self.refreshAnnotations) self.annotationManager.annotationRemoved.connect( self.refreshAnnotations)
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameters: if param.isAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr(' (x, y)') if param.optional: desc += self.tr(' [optional]') wrapper = self.getWidgetWrapperFromParameter(param) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget is not None: if isinstance(param, ParameterVector): layout = QHBoxLayout() layout.setSpacing(2) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setToolTip(self.tr('Iterate over this layer')) button.setCheckable(True) layout.addWidget(button) self.iterateButtons[param.name] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) tooltips = self.alg.getParameterDescriptions() widget.setToolTip(tooltips.get(param.name, param.description)) if type(widget) is QCheckBox: # checkbox widget - so description is embedded in widget rather than a separate # label widget.setText(desc) else: label = QLabel(desc) # label.setToolTip(tooltip) self.labels[param.name] = label if param.isAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) if param.isAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.outputs: if output.hidden: continue label = QLabel(output.description) widget = OutputSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (OutputRaster, OutputVector, OutputTable)): check = QCheckBox() check.setText(self.tr('Open output file after running algorithm')) check.setChecked(True) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name] = check self.outputWidgets[output.name] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
class PlanetExplorer(object): def __init__(self, iface): self.iface = iface # Initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', '{0}Plugin_{1}.qm'.format(PE, locale) ) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr('&{0}'.format(P_E)) self.toolbar = None # noinspection PyTypeChecker self.explorer_dock_widget = None self._terms_browser = None readSettings() if is_segments_write_key_valid(): analytics.write_key = segments_write_key() if is_sentry_dsn_valid(): sentry_sdk.init(sentry_dsn(), default_integrations=False) self.qgis_hook = sys.excepthook def plugin_hook(t, value, tb): trace = "".join(traceback.format_exception(t, value, tb)) if PLUGIN_NAMESPACE in trace.lower(): try: sentry_sdk.capture_exception(value) except: pass # we swallow all exceptions here, to avoid entering an endless loop self.qgis_hook(t, value, tb) sys.excepthook = plugin_hook metadataFile = os.path.join(os.path.dirname(__file__), "metadata.txt") cp = configparser.ConfigParser() with codecs.open(metadataFile, "r", "utf8") as f: cp.read_file(f) if is_sentry_dsn_valid(): version = cp["general"]["version"] with sentry_sdk.configure_scope() as scope: scope.set_context("plugin_version", version) scope.set_context("qgis_version", Qgis.QGIS_VERSION) # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(PE, message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToWebMenu( self.menu, action) self.actions.append(action) return action # noinspection PyPep8Naming def initGui(self): self.toolbar = self.iface.addToolBar(P_E) self.toolbar.setObjectName(P_E) self.showdailyimages_act = self.add_action( os.path.join(plugin_path, "resources", "search.svg"), text=self.tr(P_E), callback=toggle_images_search, add_to_menu=True, add_to_toolbar=True, parent=self.iface.mainWindow()) self.showbasemaps_act = self.add_action( os.path.join(plugin_path, "resources", "basemap.svg"), text=self.tr("Show Basemaps Search"), callback=toggle_mosaics_search, add_to_menu=True, add_to_toolbar=True, parent=self.iface.mainWindow()) self.showinspector_act = self.add_action( os.path.join(plugin_path, "resources", "inspector.svg"), text=self.tr("Show Planet Inspector..."), callback=toggle_inspector, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow()) self.showtasking_act = self.add_action( os.path.join(plugin_path, "resources", "tasking.svg"), text=self.tr("Show Tasking..."), callback=toggle_tasking_widget, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow()) self.add_central_toolbar_button() self.showorders_act = self.add_action( os.path.join(plugin_path, "resources", "orders.svg"), text=self.tr("Show Orders Monitor..."), callback=toggle_orders_monitor, add_to_menu=False, add_to_toolbar=True, parent=self.iface.mainWindow()) self.add_user_button() self.add_info_button() addSettingsMenu(P_E, self.iface.addPluginToWebMenu) addAboutMenu(P_E, self.iface.addPluginToWebMenu) self.provider = BasemapLayerWidgetProvider() QgsGui.layerTreeEmbeddedWidgetRegistry().addProvider(self.provider) QgsProject.instance().projectSaved.connect(self.project_saved) QgsProject.instance().layersAdded.connect(self.layers_added) QgsProject.instance().layerRemoved.connect(self.layer_removed) PlanetClient.getInstance().loginChanged.connect(self.login_changed) self.enable_buttons(False) def add_central_toolbar_button(self): widget = QWidget() widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) layout = QHBoxLayout() layout.addStretch() self.btnLogin = QPushButton() palette = self.btnLogin.palette() palette.setColor(QPalette.Button, PLANET_COLOR) self.btnLogin.setPalette(palette) self.btnLogin.setText("Log in") #self.btnLogin.setAutoRaise(True) self.btnLogin.setAttribute(Qt.WA_TranslucentBackground) self.btnLogin.clicked.connect(self.btn_login_clicked) icon = QIcon(os.path.join(plugin_path, "resources", "planet-logo-p.svg")) labelIcon = QLabel() labelIcon.setPixmap(icon.pixmap(QSize(16, 16))) layout.addWidget(labelIcon) self.labelLoggedIn = QLabel() self.labelLoggedIn.setText("") layout.addWidget(self.labelLoggedIn) layout.addWidget(self.btnLogin) layout.addStretch() widget.setLayout(layout) self.toolbar.addWidget(widget) def btn_login_clicked(self): if PlanetClient.getInstance().has_api_key(): self.logout() else: self.login() def layer_removed(self, layer): self.provider.layerWasRemoved(layer) def layers_added(self, layers): for layer in layers: add_widget_to_layer(layer) def login_changed(self, loggedin): self.provider.updateLayerWidgets() self.enable_buttons(loggedin) if not loggedin: hide_orders_monitor() hide_inspector() def add_info_button(self): info_menu = QMenu() p_sec_act = add_menu_section_action('Planet', info_menu) p_com_act = QAction(QIcon(EXT_LINK), 'planet.com', info_menu) p_com_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_COM) ) info_menu.addAction(p_com_act) p_explorer_act = QAction(QIcon(EXT_LINK), 'Planet Explorer web app', info_menu) p_explorer_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_EXPLORER) ) info_menu.addAction(p_explorer_act) p_sat_act = QAction(QIcon(EXT_LINK), 'Satellite specs PDF', info_menu) p_sat_act.triggered[bool].connect( lambda: open_link_with_browser(SAT_SPECS_PDF) ) info_menu.addAction(p_sat_act) p_support_act = QAction(QIcon(EXT_LINK), 'Support Community', info_menu) p_support_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SUPPORT_COMMUNITY) ) info_menu.addAction(p_support_act) p_whatsnew_act = QAction(QIcon(EXT_LINK), "What's new", info_menu) p_whatsnew_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_INTEGRATIONS) ) info_menu.addAction(p_whatsnew_act) p_sales_act = QAction(QIcon(EXT_LINK), "Sales", info_menu) p_sales_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SALES) ) info_menu.addAction(p_sales_act) add_menu_section_action('Documentation', info_menu) terms_act = QAction('Terms', info_menu) terms_act.triggered[bool].connect(self.show_terms) info_menu.addAction(terms_act) btn = QToolButton() btn.setIcon(QIcon(os.path.join(plugin_path, "resources", "info.svg"),)) btn.setMenu(info_menu) btn.setPopupMode(QToolButton.MenuButtonPopup) # Also show menu on click, to keep disclosure triangle visible btn.clicked.connect(btn.showMenu) self.toolbar.addWidget(btn) def add_user_button(self): user_menu = QMenu() self.acct_act = QAction(QIcon(EXT_LINK), 'Account', user_menu) self.acct_act.triggered[bool].connect( lambda: QDesktopServices.openUrl(QUrl(ACCOUNT_URL)) ) user_menu.addAction(self.acct_act) self.logout_act = QAction('Logout', user_menu) self.logout_act.triggered[bool].connect(self.logout) user_menu.addAction(self.logout_act) self.user_button = QToolButton() self.user_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon); self.user_button.setIcon(QIcon(os.path.join(plugin_path, "resources", "account.svg"),)) self.user_button.setMenu(user_menu) self.user_button.setPopupMode(QToolButton.MenuButtonPopup) # Also show menu on click, to keep disclosure triangle visible self.user_button.clicked.connect(self.user_button.showMenu) self.toolbar.addWidget(self.user_button) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" PlanetClient.getInstance().log_out() self.provider.updateLayerWidgets() removeSettingsMenu(P_E, self.iface.removePluginWebMenu) # removeHelpMenu(P_E, self.iface.removePluginWebMenu) removeAboutMenu(P_E, self.iface.removePluginWebMenu) for action in self.actions: self.iface.removePluginWebMenu( self.tr('&{0}'.format(P_E)), action) self.iface.removeToolBarIcon(action) # remove the toolbar if self.toolbar is not None: del self.toolbar remove_inspector() remove_explorer() remove_orders_monitor() remove_tasking_widget() sys.excepthook = self.qgis_hook QgsProject.instance().projectSaved.disconnect(self.project_saved) QgsProject.instance().layersAdded.disconnect(self.layers_added) QgsProject.instance().layerRemoved.disconnect(self.layer_removed) # ----------------------------------------------------------- def show_terms(self, _): if self._terms_browser is None: self._terms_browser = QTextBrowser() self._terms_browser.setReadOnly(True) self._terms_browser.setOpenExternalLinks(True) self._terms_browser.setMinimumSize(600, 700) # TODO: Template terms.html first section, per subscription level # Collect subscription info from self.p_client.user self._terms_browser.setSource( QUrl('qrc:/plugins/planet_explorer/terms.html')) self._terms_browser.setWindowModality(Qt.ApplicationModal) self._terms_browser.show() def login(self): show_explorer() def logout(self): PlanetClient.getInstance().log_out() def enable_buttons(self, loggedin): self.btnLogin.setVisible(not loggedin) labelText = (f"<b>Welcome to Planet</b>" if not loggedin else "<b>Planet</b>") self.labelLoggedIn.setText(labelText) self.showdailyimages_act.setEnabled(loggedin) self.showbasemaps_act.setEnabled(loggedin) self.showinspector_act.setEnabled(loggedin) self.showorders_act.setEnabled(loggedin) self.showtasking_act.setEnabled(loggedin) self.user_button.setEnabled(loggedin) self.user_button.setText( PlanetClient.getInstance().user()['user_name'] if loggedin else "") if loggedin: self.showdailyimages_act.setToolTip("Show / Hide the Planet Imagery Search Panel") self.showbasemaps_act.setToolTip("Show / Hide the Planet Basemaps Search Panel") self.showorders_act.setToolTip("Show / Hide the Order Status Panel") self.showinspector_act.setToolTip("Show / Hide the Planet Inspector Panel") self.showtasking_act.setToolTip("Show / Hide the Tasking Panel") else: self.showdailyimages_act.setToolTip("Login to access Imagery Search") self.showbasemaps_act.setToolTip("Login to access Basemaps Search") self.showorders_act.setToolTip("Login to access Order Status") self.showinspector_act.setToolTip("Login to access Planet Inspector") self.showtasking_act.setToolTip("Login to access Tasking Panel") def project_saved(self): if PlanetClient.getInstance().has_api_key(): def resave(): path = QgsProject.instance().absoluteFilePath() if path.lower().endswith(".qgs"): with open(path) as f: s = f.read() with open(path, "w") as f: f.write(s.replace(PlanetClient.getInstance().api_key(), "")) else: tmpfilename = path + ".temp" qgsfilename = os.path.splitext(os.path.basename(path))[0] + ".qgs" with zipfile.ZipFile(path, 'r') as zin: with zipfile.ZipFile(tmpfilename, 'w') as zout: zout.comment = zin.comment for item in zin.infolist(): if not item.filename.lower().endswith(".qgs"): zout.writestr(item, zin.read(item.filename)) else: s = zin.read(item.filename).decode("utf-8") s = s.replace(PlanetClient.getInstance().api_key(), "") qgsfilename = item.filename os.remove(path) os.rename(tmpfilename, path) with zipfile.ZipFile(path, mode='a', compression=zipfile.ZIP_DEFLATED) as zf: zf.writestr(qgsfilename, s) QTimer.singleShot(100, resave)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self._variables_scope = None # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.propertiesDock.setWindowTitle(self.tr("Model Properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView( None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip( self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.variables_dock = QgsDockWidget(self) self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.variables_dock.setObjectName("variablesDock") self.variables_dock_contents = QWidget() vl_v = QVBoxLayout() vl_v.setContentsMargins(0, 0, 0, 0) self.variables_editor = QgsVariableEditorWidget() vl_v.addWidget(self.variables_editor) self.variables_dock_contents.setLayout(vl_v) self.variables_dock.setWidget(self.variables_dock_contents) self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock) self.variables_dock.setWindowTitle(self.tr("Variables")) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.tabifyDockWidget(self.propertiesDock, self.variables_dock) self.variables_editor.scopeChanged.connect(self.variables_changed) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.toolbutton_export_to_script = QToolButton() self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup) self.export_to_script_algorithm_action = QAction( QCoreApplication.translate('ModelerDialog', 'Export as Script Algorithm…')) self.toolbutton_export_to_script.addActions( [self.export_to_script_algorithm_action]) self.mToolbar.insertWidget(self.mActionExportImage, self.toolbutton_export_to_script) self.export_to_script_algorithm_action.triggered.connect( self.export_as_script_algorithm) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.toolbutton_export_to_script.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState( settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry( settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): def alg_dropped(algorithm_id, pos): alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm_id) if alg is not None: self._addAlgorithm(alg, pos) else: assert False, algorithm_id def input_dropped(id, pos): if id in [ param.id() for param in QgsApplication.instance(). processingRegistry().parameterTypes() ]: self.addInputOfType(itemId, pos) if event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data( 'application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() QTimer.singleShot( 0, lambda id=algorithm_id, pos=self.view.mapToScene(event.pos( )): alg_dropped(id, pos)) event.accept() elif event.mimeData().hasText(): itemId = event.mimeData().text() QTimer.singleShot(0, lambda id=itemId, pos=self.view.mapToScene( event.pos()): input_dropped(id, pos)) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue( self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue( self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel( self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) filters = QgsProcessingToolboxProxyModel.Filters( QgsProcessingToolboxProxyModel.FilterModeler) if ProcessingConfig.getSetting( ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues self.algorithmTree.setFilters(filters) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText( QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.update_variables_gui() self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self. tr('There are unsaved changes in this model. Do you want to keep those?' ), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def update_variables_gui(self): variables_scope = QgsExpressionContextScope(self.tr('Model Variables')) for k, v in self.model.variables().items(): variables_scope.setVariable(k, v) variables_context = QgsExpressionContext() variables_context.appendScope(variables_scope) self.variables_editor.setContext(variables_context) self.variables_editor.setEditableScopeIndex(0) def variables_changed(self): self.model.setVariables(self.variables_editor.variablesInActiveScope()) def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage( "", self. tr("Model doesn't contain any algorithm and/or parameter and can't be executed" ), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow()) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById( PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage( "", self.tr( "Successfully exported model as image to <a href=\"{}\">{}</a>" ).format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage( "", self.tr( "Successfully exported model as PDF to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage( "", self.tr( "Successfully exported model as SVG to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage( "", self. tr("Successfully exported model as python script to <a href=\"{}\">{}</a>" ).format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving')) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning( self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format( str(sys.exc_info()[1]))) else: QMessageBox.warning( self, self.tr("Can't save model"), QCoreApplication. translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option." ))) return self.update_model.emit() if saveAs: self.bar.pushMessage( "", self.tr( "Model was correctly saved to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) else: self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName( self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.update_variables_gui() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage( self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical( self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([ i.position().x() for i in list(self.model.parameterComponents().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted( QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition( alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([ alg.position().x() for alg in list(self.model.childAlgorithms().values()) ]) maxY = max([ alg.position().y() for alg in list(self.model.childAlgorithms().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def export_as_script_algorithm(self): dlg = ScriptEditorDialog(None) dlg.editor.setText('\n'.join( self.model.asPythonCode( QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4))) dlg.show()
def add_info_button(self): info_menu = QMenu() p_sec_act = add_menu_section_action('Planet', info_menu) p_com_act = QAction(QIcon(EXT_LINK), 'planet.com', info_menu) p_com_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_COM) ) info_menu.addAction(p_com_act) p_explorer_act = QAction(QIcon(EXT_LINK), 'Planet Explorer web app', info_menu) p_explorer_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_EXPLORER) ) info_menu.addAction(p_explorer_act) p_sat_act = QAction(QIcon(EXT_LINK), 'Satellite specs PDF', info_menu) p_sat_act.triggered[bool].connect( lambda: open_link_with_browser(SAT_SPECS_PDF) ) info_menu.addAction(p_sat_act) p_support_act = QAction(QIcon(EXT_LINK), 'Support Community', info_menu) p_support_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SUPPORT_COMMUNITY) ) info_menu.addAction(p_support_act) p_whatsnew_act = QAction(QIcon(EXT_LINK), "What's new", info_menu) p_whatsnew_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_INTEGRATIONS) ) info_menu.addAction(p_whatsnew_act) p_sales_act = QAction(QIcon(EXT_LINK), "Sales", info_menu) p_sales_act.triggered[bool].connect( lambda: open_link_with_browser(PLANET_SALES) ) info_menu.addAction(p_sales_act) add_menu_section_action('Documentation', info_menu) terms_act = QAction('Terms', info_menu) terms_act.triggered[bool].connect(self.show_terms) info_menu.addAction(terms_act) btn = QToolButton() btn.setIcon(QIcon(os.path.join(plugin_path, "resources", "info.svg"),)) btn.setMenu(info_menu) btn.setPopupMode(QToolButton.MenuButtonPopup) # Also show menu on click, to keep disclosure triangle visible btn.clicked.connect(btn.showMenu) self.toolbar.addWidget(btn)
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameters: if param.isAdvanced: self.grpAdvanced.show() break # Create widgets and put them in layouts for param in self.alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr(' (x, y)') if param.optional: desc += self.tr(' [optional]') wrapper = self.getWidgetWrapperFromParameter(param) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget is not None: if isinstance(param, ParameterVector): layout = QHBoxLayout() layout.setSpacing(2) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon( os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setToolTip(self.tr('Iterate over this layer')) button.setCheckable(True) layout.addWidget(button) self.iterateButtons[param.name] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) tooltips = self.alg.getParameterDescriptions() widget.setToolTip(tooltips.get(param.name, param.description)) if type(widget) is QCheckBox: # checkbox widget - so description is embedded in widget rather than a separate # label widget.setText(desc) else: label = QLabel(desc) # label.setToolTip(tooltip) self.labels[param.name] = label if param.isAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) if param.isAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget(self.layoutMain.count() - 2, widget) for output in self.alg.outputs: if output.hidden: continue label = QLabel(output.description) widget = OutputSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (OutputRaster, OutputVector, OutputTable)): check = QCheckBox() check.setText( self.tr('Open output file after running algorithm')) check.setChecked(True) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name] = check self.outputWidgets[output.name] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
class ForeignKeyLineEdit(QLineEdit): """ Line edit that enables the browsing of related entities defined through foreign key constraint. """ def __init__(self, column, parent=None, pixmap=None, host=None): """ Class constructor. :param column: Column object containing foreign key information. :type column: BaseColumn :param parent: Parent widget for the control. :type parent: QWidget :param pixmap: Pixmap to use for the line edit button. :type pixmap: QPixmap """ QLineEdit.__init__(self, parent) self.column = column self._entity = self.column.entity self.entity_dialog = host # Configure load button self.btn_load = QToolButton(parent) self.btn_load.setCursor(Qt.PointingHandCursor) self.btn_load.setFocusPolicy(Qt.NoFocus) px = GuiUtils.get_icon_pixmap('select_record.png') if not pixmap is None: px = pixmap self.btn_load.setIcon(QIcon(px)) self.btn_load.setIconSize(px.size()) self.btn_load.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') self.btn_load.clicked.connect(self.on_load_foreign_key_browser) clear_px = GuiUtils.get_icon_pixmap('clear.png') self.btn_clear = QToolButton(parent) self.btn_clear.setCursor(Qt.PointingHandCursor) self.btn_clear.setFocusPolicy(Qt.NoFocus) self.btn_clear.setIcon(QIcon(clear_px)) self.btn_clear.setIconSize(clear_px.size()) self.btn_clear.setStyleSheet('background: transparent; padding: 0px; ' 'border: none;') self.btn_clear.clicked.connect(self.clear_line_edit) frame_width = self.set_button_minimum_size(self.btn_load) self.set_button_minimum_size(self.btn_clear) # Ensure that text does not overlay button padding = self.btn_load.sizeHint().width() + frame_width + 1 self.setStyleSheet('padding-right: ' + str(padding * 2) + 'px;') # Set layout self.button_layout = QHBoxLayout(self) self.button_layout.addWidget(self.btn_clear, 0, Qt.AlignRight) self.button_layout.addWidget(self.btn_load, 0, Qt.AlignRight) self.button_layout.setSpacing(0) self.button_layout.setMargin(5) self.btn_clear.setVisible(False) # Readonly as text is loaded from the related entity self.setReadOnly(True) # Current model object self._current_item = None def set_button_minimum_size(self, button): """ Sets the minimum button size. :param button: The button to be set. :type button: QToolButton :return: Returns the frame width of the button :rtype: Integer """ frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) msz = self.minimumSizeHint() self.setMinimumSize( max( msz.width(), button.sizeHint().height() + frame_width * 2 + 2 ), max( msz.height(), button.sizeHint().height() + frame_width * 2 + 2 ) ) return frame_width @property def current_item(self): return self._current_item @current_item.setter def current_item(self, value): # Update display every time the current item is changed. self._current_item = value self.format_display() @property def entity(self): """ :return: Returns the entity object corresponding to this widget. :rtype: Entity """ return self._entity def clear_line_edit(self): """ Clears the text in the line edit. """ self.clear() self.hide_clear_button() def hide_clear_button(self): """ Hides the clear button. """ self.btn_clear.setVisible(False) self.button_layout.setStretch(0, 0) def show_clear_button(self): """ Shows the clear button if a text exists. """ if len(self.text()) > 0: self.btn_clear.setVisible(True) self.button_layout.setStretch(0, 5) def on_load_foreign_key_browser(self): """ Slot raised to load browser for selecting foreign key entities. To be implemented by subclasses. """ raise NotImplementedError def format_display(self): """ Extract object values to show in the line edit based on the specified display columns. """ raise NotImplementedError def parent_entity_model(self): """ :return: Returns the database model corresponding to the parent table of the relation defined by this column. Please note that the database model will not contain relationship configurations in its attributes. :rtype: object """ entity = self.column.entity_relation.parent return entity_model(entity, entity_only=True) def load_current_item_from_id(self, id): """ Loads the current item from the id corresponding to the primary key. :param id: Primary key of the referenced entity. :type id: int """ QApplication.processEvents() model = self.parent_entity_model() if model is None: return model_obj = model() res = model_obj.queryObject().filter(model.id == id).first() if not res is None: self.current_item = res
class FeatureSelectorWidget(QWidget): feature_identified = pyqtSignal(QgsFeature) def __init__(self, parent): QWidget.__init__(self, parent) edit_layout = QHBoxLayout() edit_layout.setContentsMargins(0, 0, 0, 0) edit_layout.setSpacing(2) self.setLayout(edit_layout) self.line_edit = QLineEdit(self) self.line_edit.setReadOnly(True) edit_layout.addWidget(self.line_edit) self.highlight_feature_button = QToolButton(self) self.highlight_feature_button.setPopupMode(QToolButton.MenuButtonPopup) self.highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"), "Highlight feature", self) self.scale_highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"), "Scale and highlight feature", self) self.pan_highlight_feature_action = QAction( QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"), "Pan and highlight feature", self) self.highlight_feature_button.addAction(self.highlight_feature_action) self.highlight_feature_button.addAction( self.scale_highlight_feature_action) self.highlight_feature_button.addAction( self.pan_highlight_feature_action) self.highlight_feature_button.setDefaultAction( self.highlight_feature_action) edit_layout.addWidget(self.highlight_feature_button) self.map_identification_button = QToolButton(self) self.map_identification_button.setIcon( QgsApplication.getThemeIcon("/mActionMapIdentification.svg")) self.map_identification_button.setText("Select on map") self.map_identification_button.setCheckable(True) edit_layout.addWidget(self.map_identification_button) self.map_identification_button.clicked.connect(self.map_identification) self.highlight_feature_button.triggered.connect( self.highlight_action_triggered) self.layer = None self.map_tool = None self.canvas = None self.window_widget = None self.highlight = None self.feature = QgsFeature() def set_canvas(self, map_canvas): self.map_tool = QgsMapToolIdentifyFeature(map_canvas) self.map_tool.setButton(self.map_identification_button) self.canvas = map_canvas def set_layer(self, layer): self.layer = layer def set_feature(self, feature, canvas_extent=CanvasExtent.Fixed): self.line_edit.clear() self.feature = feature if self.feature is None or not self.feature.isValid( ) or self.layer is None: return expression = QgsExpression(self.layer.displayExpression()) context = QgsExpressionContext() scope = QgsExpressionContextScope() context.appendScope(scope) scope.setFeature(feature) feature_title = expression.evaluate(context) if feature_title == "": feature_title = feature.id() self.line_edit.setText(str(feature_title)) self.highlight_feature(canvas_extent) def clear(self): self.feature = QgsFeature() self.line_edit.clear() @pyqtSlot() def map_identification(self): if self.layer is None or self.map_tool is None or self.canvas is None: return self.map_tool.setLayer(self.layer) self.canvas.setMapTool(self.map_tool) self.window_widget = QWidget.window(self) self.canvas.window().raise_() self.canvas.activateWindow() self.canvas.setFocus() self.map_tool.featureIdentified.connect( self.map_tool_feature_identified) self.map_tool.deactivated.connect(self.map_tool_deactivated) def map_tool_feature_identified(self, feature): feature = QgsFeature(feature) self.feature_identified.emit(feature) self.unset_map_tool() self.set_feature(feature) def map_tool_deactivated(self): if self.window_widget is not None: self.window_widget.raise_() self.window_widget.activateWindow() def highlight_feature(self, canvas_extent=CanvasExtent.Fixed): if self.canvas is None or not self.feature.isValid(): return geom = self.feature.geometry() if geom is None: return if canvas_extent == CanvasExtent.Scale: feature_bounding_box = geom.boundingBox() feature_bounding_box = self.canvas.mapSettings( ).layerToMapCoordinates(self.layer, feature_bounding_box) extent = self.canvas.extent() if not extent.contains(feature_bounding_box): extent.combineExtentWith(feature_bounding_box) extent.scale(1.1) self.canvas.setExtent(extent) self.canvas.refresh() elif canvas_extent == CanvasExtent.Pan: centroid = geom.centroid() center = centroid.asPoint() center = self.canvas.mapSettings().layerToMapCoordinates( self.layer, center) self.canvas.zoomByFactor(1.0, center) # refresh is done in this method # highlight self.delete_highlight() self.highlight = QgsHighlight(self.canvas, geom, self.layer) settings = QSettings() color = QColor( settings.value("/Map/highlight/color", Qgis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int( settings.value("/Map/highlight/colorAlpha", Qgis.DEFAULT_HIGHLIGHT_COLOR.alpha())) buffer = 2 * float( settings.value("/Map/highlight/buffer", Qgis.DEFAULT_HIGHLIGHT_BUFFER_MM)) min_width = 2 * float( settings.value("/Map/highlight/min_width", Qgis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.highlight.setColor(color) # sets also fill with default alpha color.setAlpha(alpha) self.highlight.setFillColor(color) # sets fill with alpha self.highlight.setBuffer(buffer) self.highlight.setMinWidth(min_width) self.highlight.setWidth(4.0) self.highlight.show() self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.delete_highlight) self.timer.start(3000) def delete_highlight(self): if self.highlight is not None: self.highlight.hide() del self.highlight self.highlight = None def unset_map_tool(self): if self.canvas is not None and self.map_tool is not None: # this will call mapTool.deactivated self.canvas.unsetMapTool(self.map_tool) def highlight_action_triggered(self, action): self.highlight_feature_button.setDefaultAction(action) if action == self.highlight_feature_action: self.highlight_feature() elif action == self.scale_highlight_feature_action: self.highlight_feature(CanvasExtent.Scale) elif action == self.pan_highlight_feature_action: self.highlight_feature(CanvasExtent.Pan)
def initWidgets(self): # Heavy overload # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.grpAdvanced.show() break #widget_context = QgsProcessingParameterWidgetContext() #if iface is not None: # widget_context.setMapCanvas(iface.mapCanvas()) # Create widgets and put them in layouts for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue print('initWidgets - param.name(): {}'.format(param.name())) if param.isDestination(): # and param.name() != 'OUTPUT_ASC': continue else: wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent) self.wrappers[param.name()] = wrapper #widget = wrapper.widget # For compatibility with 3.x API, we need to check whether the wrapper is # the deprecated WidgetWrapper class. If not, it's the newer # QgsAbstractProcessingParameterWidgetWrapper class # TODO QGIS 4.0 - remove is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper) if not is_python_wrapper: from qgis.gui import (QgsProcessingContextGenerator, QgsProcessingParameterWidgetContext) widget_context = QgsProcessingParameterWidgetContext() if iface is not None: widget_context.setMapCanvas(iface.mapCanvas()) wrapper.setWidgetContext(widget_context) widget = wrapper.createWrappedWidget(self.processing_context) wrapper.registerProcessingContextGenerator(self.context_generator) else: widget = wrapper.widget #if self.in_place and param.name() in ('INPUT', 'OUTPUT'): # don't show the input/output parameter widgets in in-place mode # we still need to CREATE them, because other wrappers may need to interact # with them (e.g. those parameters which need the input layer for field # selections/crs properties/etc) # continue if widget is not None: if is_python_wrapper: widget.setToolTip(param.toolTip()) if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(6) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) button.setToolTip(self.tr('Iterate over this layer, creating a separate output for every feature in the layer')) button.setCheckable(True) layout.addWidget(button) layout.setAlignment(button, Qt.AlignTop) self.iterateButtons[param.name()] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) label = None if not is_python_wrapper: label = wrapper.createWrappedLabel() else: label = wrapper.label if label is not None: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) elif is_python_wrapper: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr(' (x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') widget.setText(desc) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue #if self.in_place and param.name() in ('INPUT', 'OUTPUT'): # continue label = QLabel(output.description()) #print('initWidgets 2 - param.name(): {}'.format(param.name())) widget = DestinationSelectionPanel(output, self.alg) # TODO, overload self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination # alk: checkboxes for Chloe handling ,ChloeCSVParameterFileDestination, ChloeASCParameterFileDestination, ChloeParameterFolderDestination) ): check = QCheckBox() check.setText(QCoreApplication.translate('ParametersPanel', 'Open output file(s) after running algorithm')) def skipOutputChanged(checkbox, skipped): checkbox.setEnabled(not skipped) if skipped: checkbox.setChecked(False) check.setChecked(not widget.outputIsSkipped()) check.setEnabled(not widget.outputIsSkipped()) widget.skipOutputChanged.connect(partial(skipOutputChanged, check)) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name()] = check # initial state if hasattr(output,'addToMapDefaultState'): check.setChecked(output.addToMapDefaultState) widget.setToolTip(param.toolTip()) self.outputWidgets[output.name()] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) # # alk: checkboxes for Chloe handling # for output in self.alg.destinationParameterDefinitions(): # if output.flags() & QgsProcessingParameterDefinition.FlagHidden: # continue # if isinstance(output, (ChloeCSVParameterFileDestination)) or isinstance(output, (ChloeASCParameterFileDestination)): # check = QCheckBox() # check.setText(QCoreApplication.translate('ParametersPanel', 'Open output file(s) after running algorithm')) # def skipOutputChanged(checkbox, skipped): # checkbox.setEnabled(not skipped) # if skipped: # checkbox.setChecked(False) # check.setChecked(not widget.outputIsSkipped()) # check.setEnabled(not widget.outputIsSkipped()) # widget.skipOutputChanged.connect(partial(skipOutputChanged, check)) # print(str(self.layoutMain)+1) # self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) # self.checkBoxes[output.name()] = check # # connecting alg outputLoading info with checkbox state # self.alg.outputLoading[output.name()] = check.isChecked() # def updateOutputLoadingState(alg, outputName, checkbox, state): # self.alg.outputLoading[outputName] = checkbox.isChecked() # print( outputName + " " + str(checkbox.isChecked()) + " " + str(self.alg.outputLoading) + " " + str(self.alg)) # #print(str(self.alg.parameters)) # check.stateChanged.connect(partial(updateOutputLoadingState, self, output.name(), check)) # alk: addition of wrapper special config handling # for dependancy between wrapper, i.e. changing the value # of a FileSelectionPanel entails the update of another widget for k in self.wrappers: w = self.wrappers[k] if hasattr(w,'getParentWidgetConfig'): print(str(w) + " " + "getParentWidgetConfig") config = w.getParentWidgetConfig() if config != None: p = self.wrappers[config['paramName']] m = getattr(w, config['refreshMethod']) if m!=None: print(str(p) + " " + str(p.widget)) # todo generalize valueChanged handling # to any type of widget componant if isinstance(p.widget, FileSelectionPanel): p.widget.leText.textChanged.connect(m) elif isinstance(p, RasterWidgetWrapper): try: p.combo.valueChanged.connect(m) # QGIS 3.8 version except: p.combo.currentIndexChanged.connect(m) # QGIS LTR 3.4
def initWidgets(self): # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.grpAdvanced.show() break widget_context = QgsProcessingParameterWidgetContext() if iface is not None: widget_context.setMapCanvas(iface.mapCanvas()) # Create widgets and put them in layouts for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.isDestination(): continue else: wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent) self.wrappers[param.name()] = wrapper # For compatibility with 3.x API, we need to check whether the wrapper is # the deprecated WidgetWrapper class. If not, it's the newer # QgsAbstractProcessingParameterWidgetWrapper class # TODO QGIS 4.0 - remove is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper) if not is_python_wrapper: wrapper.setWidgetContext(widget_context) widget = wrapper.createWrappedWidget(self.processing_context) wrapper.registerProcessingContextGenerator(self.context_generator) else: widget = wrapper.widget if self.in_place and param.name() in ('INPUT', 'OUTPUT'): # don't show the input/output parameter widgets in in-place mode # we still need to CREATE them, because other wrappers may need to interact # with them (e.g. those parameters which need the input layer for field # selections/crs properties/etc) continue if widget is not None: if is_python_wrapper: widget.setToolTip(param.toolTip()) if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(6) layout.setMargin(0) layout.addWidget(widget) button = QToolButton() icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png')) button.setIcon(icon) button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) button.setToolTip(self.tr('Iterate over this layer, creating a separate output for every feature in the layer')) button.setCheckable(True) layout.addWidget(button) layout.setAlignment(button, Qt.AlignTop) self.iterateButtons[param.name()] = button button.toggled.connect(self.buttonToggled) widget = QWidget() widget.setLayout(layout) label = None if not is_python_wrapper: label = wrapper.createWrappedLabel() else: label = wrapper.label if label is not None: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, label) elif is_python_wrapper: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr(' (x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') widget.setText(desc) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.layoutAdvanced.addWidget(widget) else: self.layoutMain.insertWidget( self.layoutMain.count() - 2, widget) for output in self.alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if self.in_place and param.name() in ('INPUT', 'OUTPUT'): continue label = QLabel(output.description()) widget = DestinationSelectionPanel(output, self.alg) self.layoutMain.insertWidget(self.layoutMain.count() - 1, label) self.layoutMain.insertWidget(self.layoutMain.count() - 1, widget) if isinstance(output, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): check = QCheckBox() check.setText(QCoreApplication.translate('ParametersPanel', 'Open output file after running algorithm')) def skipOutputChanged(checkbox, skipped): checkbox.setEnabled(not skipped) if skipped: checkbox.setChecked(False) check.setChecked(not widget.outputIsSkipped()) check.setEnabled(not widget.outputIsSkipped()) widget.skipOutputChanged.connect(partial(skipOutputChanged, check)) self.layoutMain.insertWidget(self.layoutMain.count() - 1, check) self.checkBoxes[output.name()] = check widget.setToolTip(param.toolTip()) self.outputWidgets[output.name()] = widget for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values()))
def update_layers(self): if self.tabWidget.currentIndex() != 2: return if self.layerSelection.currentIndex() == 3: # by selection string self.selectionStringLineEdit.setEnabled(True) else: self.selectionStringLineEdit.setEnabled(False) if self.layerSelection.currentIndex() == 0: # visible layers layers = self.activeRasterLayers(0) elif self.layerSelection.currentIndex() == 3: # by selection string layers = self.activeRasterLayers(3) else: layers = self.activeRasterLayers(1) # All layers self.selectionTable.blockSignals(True) self.selectionTable.clearContents() self.selectionTable.setRowCount(len(layers)) self.selectionTable.horizontalHeader().resizeSection(0, 20) self.selectionTable.horizontalHeader().resizeSection(2, 20) j = 0 for layer in layers: item = QTableWidgetItem() item.setFlags(item.flags() | Qt.ItemIsUserCheckable) if self.layerSelection.currentIndex() != 2: item.setFlags(item.flags() & ~Qt.ItemIsEnabled) item.setCheckState(Qt.Checked) else: # manual selection if layer.id() in self.layersSelected: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) self.selectionTable.setItem(j, 0, item) item = QTableWidgetItem(layer.name()) item.setData(Qt.UserRole, layer.id()) self.selectionTable.setItem(j, 1, item) activeBands = self.activeBandsForRaster(layer) button = QToolButton() button.setIcon(QIcon(':/plugins/mutant/img/bands.jpg')) # button.setIconSize(QtCore.QSize(400, 400)) button.setPopupMode(QToolButton.InstantPopup) group = QActionGroup(button) group.setExclusive(False) group.triggered.connect(self.bandSelected) if self.bandSelection.currentIndex( ) == 2 and layer.bandCount() > 1: menu = QMenu() menu.installEventFilter(self) for iband in range(1, layer.bandCount() + 1): action = QAction(str(layer.bandName(iband)), group) action.setData([layer.id(), iband, j, False]) action.setCheckable(True) action.setChecked(iband in activeBands) menu.addAction(action) if layer.bandCount() > 1: action = QAction(str(self.tr("All")), group) action.setData([layer.id(), -1, j, True]) action.setCheckable(False) menu.addAction(action) action = QAction(str(self.tr("None")), group) action.setData([layer.id(), -1, j, False]) action.setCheckable(False) menu.addAction(action) button.setMenu(menu) else: button.setEnabled(False) self.selectionTable.setCellWidget(j, 2, button) item = QTableWidgetItem(str(activeBands)) item.setToolTip(str(activeBands)) self.selectionTable.setItem(j, 3, item) j += 1 self.selectionTable.blockSignals(False)
class MirrorMap(QWidget): def __init__(self, parent=None, iface=None): QWidget.__init__(self, parent) # self.setAttribute(Qt.WA_DeleteOnClose) self.iface = iface self.layerId2canvasLayer = {} self.canvasLayers = [] self.setupUi() def closeEvent(self, event): self.scaleFactor.valueChanged.disconnect(self.onExtentsChanged) if not self.iface is None: self.iface.mapCanvas().extentsChanged.discconnect( self.onExtentsChanged) self.iface.mapCanvas().mapRenderer( ).destinationCrsChanged.disconnect(self.onCrsChanged) self.iface.mapCanvas().mapRenderer().mapUnitsChanged.disconnect( self.onCrsChanged) self.iface.mapCanvas().mapRenderer( ).hasCrsTransformEnabled.disconnect(self.onCrsTransformEnabled) QgsProject.instance().layerWillBeRemoved.disconnect(self.delLayer) self.iface.currentLayerChanged.disconnect(self.refreshLayerButtons) self.closed.emit() return QWidget.closeEvent(self, event) def setupUi(self): self.setObjectName("mirrormap") gridLayout = QGridLayout(self) gridLayout.setContentsMargins(0, 0, gridLayout.verticalSpacing(), gridLayout.verticalSpacing()) self.canvas = QgsMapCanvas(self) self.canvas.setCanvasColor(QColor(255, 255, 255)) settings = QSettings() gridLayout.addWidget(self.canvas, 0, 0, 1, 5) self.addLayerBtn = QToolButton(self) # self.addLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon ) # self.addLayerBtn.setText("Add current layer") self.addLayerBtn.setIcon(GuiUtils.get_icon("add.png")) self.addLayerBtn.clicked.connect(self.tool_add_layer) gridLayout.addWidget(self.addLayerBtn, 1, 0, 1, 1) self.delLayerBtn = QToolButton(self) # self.delLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon ) # self.delLayerBtn.setText("Remove current layer") self.delLayerBtn.setIcon(GuiUtils.get_icon("remove.png")) self.delLayerBtn.clicked.connect(self.tool_remove_layer) gridLayout.addWidget(self.delLayerBtn, 1, 1, 1, 1) self.renderCheck = QCheckBox("Render", self) self.renderCheck.toggled.connect(self.toggleRender) self.renderCheck.setChecked(True) gridLayout.addWidget(self.renderCheck, 1, 2, 1, 1) self.scaleFactorLabel = QLabel(self) self.scaleFactorLabel.setText("Scale factor:") self.scaleFactorLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) gridLayout.addWidget(self.scaleFactorLabel, 1, 3, 1, 1) self.scaleFactor = QDoubleSpinBox(self) self.scaleFactor.setMinimum(0.0) self.scaleFactor.setMaximum(1000.0) self.scaleFactor.setDecimals(3) self.scaleFactor.setValue(1) self.scaleFactor.setObjectName("scaleFactor") self.scaleFactor.setSingleStep(.05) gridLayout.addWidget(self.scaleFactor, 1, 4, 1, 1) self.scaleFactor.valueChanged.connect(self.onExtentsChanged) # Add a default pan tool self.toolPan = QgsMapToolPan(self.canvas) self.canvas.setMapTool(self.toolPan) self.scaleFactor.valueChanged.connect(self.onExtentsChanged) self.set_iface(self.iface) def toggleRender(self, enabled): self.canvas.setRenderFlag(enabled) def extent(self): """ :return: Current extents of the map canvas view. """ return self.canvas.extent() def canvas_layers(self): """ :return: Layers currently in the canvas. :rtype: list """ return self.canvasLayers def on_canvas_refreshed(self): """ """ self.refresh_layers() def tool_add_layer(self): self.addLayer() def tool_remove_layer(self): self.delLayer() def set_iface(self, iface): if iface is None: return self.iface = iface self.iface.mapCanvas().extentsChanged.connect(self.onExtentsChanged) # self.iface.mapCanvas().mapCanvasRefreshed.connect(self.on_canvas_refreshed) self.iface.mapCanvas().destinationCrsChanged.connect(self.onCrsChanged) QgsProject.instance().layerWillBeRemoved.connect(self.delLayer) self.iface.currentLayerChanged.connect(self.refreshLayerButtons) self.refreshLayerButtons() self.onExtentsChanged() self.onCrsChanged() def refresh_layers(self): """ Checks if the layers in the canvas list have already been added. If not, then add to the property viewer canvas. """ for ly in self.iface.mapCanvas().layers(): layer_id = self._layerId(ly) if layer_id not in self.layerId2canvasLayer: self.addLayer(layer_id) # QCoreApplication.processEvents(QEventLoop.ExcludeSocketNotifiers|QEventLoop.ExcludeUserInputEvents) def onExtentsChanged(self): prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) self.canvas.setExtent(self.iface.mapCanvas().extent()) self.canvas.zoomByFactor(self.scaleFactor.value()) # self.canvas.refresh() self.canvas.setRenderFlag(prevFlag) def onCrsChanged(self): self.canvas.setDestinationCrs( self.iface.mapCanvas().mapSettings().destinationCrs()) def refreshLayerButtons(self): layer = self.iface.activeLayer() isLayerSelected = layer != None hasLayer = False for l in self.canvas.layers(): if l == layer: hasLayer = True break self.addLayerBtn.setEnabled(isLayerSelected and not hasLayer) self.delLayerBtn.setEnabled(isLayerSelected and hasLayer) def getLayerSet(self): return [self._layerId(x.layer()) for x in self.canvasLayers] def setLayerSet(self, layerIds=None): prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) if layerIds == None: self.layerId2canvasLayer = {} self.canvasLayers = [] self.canvas.setLayers([]) else: for lid in layerIds: self.addLayer(lid) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def addLayer(self, layerId=None): if layerId == None: layer = self.iface.activeLayer() else: layer = QgsProject.instance().mapLayer(layerId) if layer == None: return prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) # add the layer to the map canvas layer set self.canvasLayers = [] id2cl_dict = {} for l in self.iface.mapCanvas().layers(): lid = self._layerId(l) if lid in self.layerId2canvasLayer: # previously added cl = self.layerId2canvasLayer[lid] elif l == layer: # Selected layer cl = layer else: continue id2cl_dict[lid] = cl self.canvasLayers.append(cl) self.layerId2canvasLayer = id2cl_dict self.canvas.setLayers(self.canvasLayers) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def delLayer(self, layerId=None): if layerId == None: layer = self.iface.activeLayer() if layer == None: return layerId = self._layerId(layer) # remove the layer from the map canvas layer set if layerId not in self.layerId2canvasLayer: return prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) cl = self.layerId2canvasLayer[layerId] del self.layerId2canvasLayer[layerId] self.canvasLayers.remove(cl) self.canvas.setLayers(self.canvasLayers) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def _layerId(self, layer): if hasattr(layer, 'id'): return layer.id() return layer.getLayerID()
class searchBar(): def __init__(self, iface, TOMsSearchBar, proposalsManager): TOMsMessageLog.logMessage("In searchBar", level=Qgis.Info) # Save reference to the QGIS interface self.iface = iface self.canvas = self.iface.mapCanvas() self.TOMsSearchBar = TOMsSearchBar self.proposalsManager = proposalsManager self.tool = TOMsInstantPrintTool(self.iface, self.proposalsManager) self.initSearchBar() # https: // gis.stackexchange.com / questions / 244584 / adding - textbox - to - qgis - plugin - toolbar def initSearchBar(self): TOMsMessageLog.logMessage("In initSearchBox:", level=Qgis.Info) self.initialPass = True self.gazetteerStringList = [] # Create & add a textbox self.searchTextbox = QLineEdit(self.iface.mainWindow()) # Set width self.searchTextbox.setFixedWidth(250) # Add textbox to toolbar self.txtEntry = self.TOMsSearchBar.addWidget(self.searchTextbox) #self.txtEntry.setToolTip(self.tr(u'Enter Street Name')) self.searchTextbox.textChanged.connect(self.doLookupItem) self.actionGoToItem = QAction(QIcon(":/plugins/TOMs/resources/magnifyingGlass.png"), QCoreApplication.translate("MyPlugin", "Start TOMs"), self.iface.mainWindow()) self.TOMsSearchBar.addAction(self.actionGoToItem) self.actionGoToItem.triggered.connect(self.doGoToItem) self.actionGoToItem.setCheckable(True) # Add in details of the Instant Print plugin self.toolButton = QToolButton(self.iface.mainWindow()) self.toolButton.setIcon(QIcon(":/plugins/TOMs/InstantPrint/icons/icon.png")) #self.toolButton.setToolTip(self.tr("Instant Print")) self.toolButton.setCheckable(True) self.printButtonAction = self.TOMsSearchBar.addWidget(self.toolButton) """self.actionInstantPrint = QAction(QIcon(":/plugins/TOMs/InstantPrint/icons/icon.png"), QCoreApplication.translate("Print", "Print"), self.iface.mainWindow())""" self.toolButton.toggled.connect(self.__enablePrintTool) self.iface.mapCanvas().mapToolSet.connect(self.__onPrintToolSet) def enableSearchBar(self): TOMsMessageLog.logMessage("In enableSearchBar", level=Qgis.Info) self.actionGoToItem.setEnabled(True) self.toolButton.setEnabled(True) self.searchTextbox.textChanged.connect(self.doLookupItem) def disableSearchBar(self): TOMsMessageLog.logMessage("In disableSearchBar", level=Qgis.Info) self.initialPass = True self.actionGoToItem.setEnabled(False) self.toolButton.setEnabled(False) self.searchTextbox.textChanged.disconnect(self.doLookupItem) def doLookupItem(self): TOMsMessageLog.logMessage("In doLookupItem:", level=Qgis.Info) # TODO: Check whether or not a project has been opened #https: // gis.stackexchange.com / questions / 246339 / drop - down - list - qgis - plugin - based - on - keyword - search / 246347 if self.initialPass: self.setupCompleter() self.initialPass = False searchText = self.searchTextbox.text() TOMsMessageLog.logMessage("In doLookupItem: searchText " + str(searchText), level=Qgis.Info) #search_in = txt #query = "SELECT myfield1, myfield2 FROM my_table WHERE '%s' LIKE '%' || search_field || '%';" % (search_in) # access your db and run the query # run the query with while query.next() and store values in a list # feed list to resiver (combox.addItems(myList) def setupCompleter(self): # set up string list for completer TOMsMessageLog.logMessage("In setupCompleter:", level=Qgis.Info) lookupStringSet = set() # https://gis.stackexchange.com/questions/155805/qstringlist-error-in-plugin-of-qgis-2-10 self.GazetteerLayer = QgsProject.instance().mapLayersByName("StreetGazetteerRecords")[0] for row in self.GazetteerLayer.getFeatures(): streetName = row.attribute("Descriptor_") locality = row.attribute("Locality") nameString = streetName if locality: nameString = nameString + ", " + locality if nameString: TOMsMessageLog.logMessage("In setupCompleter: nameString: " + nameString, level=Qgis.Info) lookupStringSet.add(nameString) # self.gazetteerStringList.append((nameString)) completer = QCompleter() completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setFilterMode(Qt.MatchContains) self.searchTextbox.setCompleter(completer) model = QStringListModel() completer.setModel(model) model.setStringList(self.gazetteerStringList) model.setStringList(sorted(lookupStringSet)) def doGoToItem(self): TOMsMessageLog.logMessage("In doGoToItem:", level=Qgis.Info) searchText = self.searchTextbox.text() TOMsMessageLog.logMessage("In doGoToItem: searchText " + str(searchText), level=Qgis.Info) # Split out the components of the text streetName, localityName = searchText.split(',') #amendedStreetName = streetName.replace("'", "\'\'") #amendedLocalityName = localityName.replace("'", "\'\'") TOMsMessageLog.logMessage("In doGoToItem: streetName: " + str(streetName.replace("'", "\'\'")) + " locality: + " + str(localityName.replace("'", "\'\'")), level=Qgis.Info) # Now search for the street queryString = "\"Descriptor_\" = \'" + streetName.replace("'", "\'\'") + "\'" if localityName: queryString = queryString + " AND \"Locality\" = \'" + localityName.replace("'", "\'\'").lstrip() + "\'" TOMsMessageLog.logMessage("In doGoToItem: queryString: " + str(queryString), level=Qgis.Info) it = self.GazetteerLayer.selectByExpression(queryString, QgsVectorLayer.SetSelection) self.canvas.zoomToSelected(self.GazetteerLayer) """box = layer.boundingBoxOfSelected() iface.mapCanvas().setExtent(box) iface.mapCanvas().refresh()""" def unload(self): self.tool.setEnabled(False) self.tool = None self.iface.TOMsSearchBar().removeAction(self.printButtonAction) self.iface.TOMsSearchBar().removeAction(self.actionGoToItem) def __enablePrintTool(self, active): self.tool.setEnabled(active) def __onPrintToolSet(self, tool): if tool != self.tool: self.toolButton.setChecked(False)