def _setupUI(self): self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # unselected widget self.unselected_widget = QListWidget(self) self._set_list_widget_defaults(self.unselected_widget) unselected_label = QLabel() unselected_label.setText("Unselected") unselected_label.setAlignment(Qt.AlignCenter) unselected_label.setFont(italic_font) unselected_v_layout = QVBoxLayout() unselected_v_layout.addWidget(unselected_label) unselected_v_layout.addWidget(self.unselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText("Selected") selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton(">>") self.deselect_all_btn = SmallQPushButton("<<") self.select_btn = SmallQPushButton(">") self.deselect_btn = SmallQPushButton("<") self.select_btn.setToolTip("Add the selected items") self.deselect_btn.setToolTip("Remove the selected items") self.select_all_btn.setToolTip("Add all") self.deselect_all_btn.setToolTip("Remove all") # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(unselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout)
class ProgressBarLogger(QDialog): """A simple dialog with a progress bar and a label""" def __init__(self, title = None): QDialog.__init__(self, None) if title is not None: self.setWindowTitle(title) self.__label = QLabel(self) self.__layout = QVBoxLayout() self.__layout.addWidget(self.__label) self.__progress = QProgressBar(self) self.__layout.addWidget(self.__progress) self.setLayout(self.__layout) self.resize(600, 70) self.setFixedSize(600, 70) self.__progress.hide() self.show() def set_text(self, t): """Gets called when a text is to be logged""" if isinstance(t, tuple): lvl, msg = t else: msg = t self.__label.setText(msg) QCoreApplication.processEvents() def set_progress(self, i, n): """Gets called when there is a progression""" self.__progress.show() self.__progress.setMinimum(0) self.__progress.setMaximum(n) self.__progress.setValue(i) QCoreApplication.processEvents()
class XYDialog(QDialog): crs = None def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr('XY Point drawing tool')) self.X = QLineEdit() self.Y = QLineEdit() X_val = QDoubleValidator() Y_val = QDoubleValidator() self.X.setValidator(X_val) self.Y.setValidator(Y_val) self.crsButton = QPushButton("Projection") self.crsButton.clicked.connect(self.changeCRS) self.crsLabel = QLabel("") buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) grid = QGridLayout() grid.addWidget(QLabel("X"), 0, 0) grid.addWidget(QLabel("Y"), 0, 1) grid.addWidget(self.X, 1, 0) grid.addWidget(self.Y, 1, 1) grid.addWidget(self.crsButton, 2, 0) grid.addWidget(self.crsLabel, 2, 1) grid.addWidget(buttons, 3, 0, 1, 2) self.setLayout(grid) def changeCRS(self): projSelector = QgsProjectionSelectionDialog() projSelector.exec_() self.crs = projSelector.crs() self.crsLabel.setText(self.crs.authid()) def getPoint(self, crs): # fix_print_with_import print(crs) dialog = XYDialog() dialog.crs = crs dialog.crsLabel.setText(crs.authid()) result = dialog.exec_() X = 0 Y = 0 if dialog.X.text().strip() and dialog.Y.text().strip(): X = float(dialog.X.text()) Y = float(dialog.Y.text()) return ([QgsPointXY(X, Y), dialog.crs], result == QDialog.Accepted)
def treeItemClicked(self, item): if item.childCount(): return color = {"MODIFIED": QColor(255, 170, 0), "ADDED":Qt.green, "REMOVED":Qt.red , "NO_CHANGE":Qt.white} changeTypeName = ["", "ADDED", "MODIFIED", "REMOVED"] path = item.text(0) if path not in self.changes: return oldfeature = self.changes[path].oldfeature newfeature = self.changes[path].newfeature changetype = self.changes[path].changetype self.attributesTable.clear() self.attributesTable.verticalHeader().show() self.attributesTable.horizontalHeader().show() self.attributesTable.setRowCount(len(newfeature)) self.attributesTable.setVerticalHeaderLabels([a for a in newfeature]) self.attributesTable.setHorizontalHeaderLabels(["Old value", "New value", "Change type"]) for i, attrib in enumerate(newfeature): self.attributesTable.setItem(i, 0, DiffItem(oldfeature.get(attrib, None))) self.attributesTable.setItem(i, 1, DiffItem(newfeature.get(attrib, None))) attribChangeType = changeTypeName[changetype] isChangedGeom = False if changetype == LOCAL_FEATURE_MODIFIED: oldvalue = oldfeature.get(attrib, None) newvalue = newfeature.get(attrib, None) try:# to avoid false change detection due to different precisions oldvalue = QgsGeometry.fromWkt(oldvalue).exportToWkt(7) newvalue = QgsGeometry.fromWkt(newvalue).exportToWkt(7) if oldvalue != newvalue and None not in [oldvalue, newvalue]: widget = QWidget() btn = QPushButton() btn.setText("View detail") g1 = QgsGeometry.fromWkt(oldvalue) g2 = QgsGeometry.fromWkt(newvalue) btn.clicked.connect(lambda: self.viewGeometryChanges(g1, g2)) label = QLabel() label.setText(attribChangeType) layout = QHBoxLayout(widget) layout.addWidget(label); layout.addWidget(btn); layout.setContentsMargins(0, 0, 0, 0) widget.setLayout(layout) self.attributesTable.setItem(i, 2, QTableWidgetItem("")) self.attributesTable.setCellWidget(i, 2, widget) isChangedGeom = True except: pass if oldvalue == newvalue: attribChangeType = "NO_CHANGE" if not isChangedGeom: self.attributesTable.setItem(i, 2, QTableWidgetItem(attribChangeType)) for col in range(3): self.attributesTable.item(i, col).setBackgroundColor(color[attribChangeType]); self.attributesTable.resizeColumnsToContents() self.attributesTable.horizontalHeader().setResizeMode(QHeaderView.Stretch)
def __init__(self, parent, alg): ParametersPanel.__init__(self, parent, alg) w = QWidget() layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(6) label = QLabel() label.setText(self.tr("GDAL/OGR console call")) layout.addWidget(label) self.text = QPlainTextEdit() self.text.setReadOnly(True) layout.addWidget(self.text) w.setLayout(layout) self.layoutMain.addWidget(w) self.connectParameterSignals() self.parametersHaveChanged()
def show_error(self, error_text): self.lstSearchResult.clear() new_widget = QLabel() new_widget.setTextFormat(Qt.RichText) new_widget.setOpenExternalLinks(True) new_widget.setWordWrap(True) new_widget.setText( u"<div align='center'> <strong>{}</strong> </div><div align='center' style='margin-top: 3px'> {} </div>".format( self.tr('Error'), error_text ) ) new_item = QListWidgetItem(self.lstSearchResult) new_item.setSizeHint(new_widget.sizeHint()) self.lstSearchResult.addItem(new_item) self.lstSearchResult.setItemWidget( new_item, new_widget )
class DistanceInputPanel(NumberInputPanel): """ Distance input panel for use outside the modeler - this input panel contains a label showing the distance unit. """ def __init__(self, param): super().__init__(param) self.label = QLabel('') label_margin = self.fontMetrics().width('X') self.layout().insertSpacing(1, label_margin / 2) self.layout().insertWidget(2, self.label) self.layout().insertSpacing(3, label_margin / 2) self.warning_label = QLabel() icon = QgsApplication.getThemeIcon('mIconWarning.svg') size = max(24, self.spnValue.height() * 0.5) self.warning_label.setPixmap(icon.pixmap(icon.actualSize(QSize(size, size)))) self.warning_label.setToolTip(self.tr('Distance is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results.')) self.layout().insertWidget(4, self.warning_label) self.layout().insertSpacing(5, label_margin) self.setUnits(QgsUnitTypes.DistanceUnknownUnit) def setUnits(self, units): self.label.setText(QgsUnitTypes.toString(units)) self.warning_label.setVisible(units == QgsUnitTypes.DistanceDegrees) def setUnitParameterValue(self, value): units = QgsUnitTypes.DistanceUnknownUnit layer = self.getLayerFromValue(value) if isinstance(layer, QgsMapLayer): units = layer.crs().mapUnits() elif isinstance(value, QgsCoordinateReferenceSystem): units = value.mapUnits() elif isinstance(value, str): crs = QgsCoordinateReferenceSystem(value) if crs.isValid(): units = crs.mapUnits() self.setUnits(units)
def search_finished_progress(self): self.lstSearchResult.takeItem(0) if self.lstSearchResult.count() == 0: new_widget = QLabel() new_widget.setTextFormat(Qt.RichText) new_widget.setOpenExternalLinks(True) new_widget.setWordWrap(True) new_widget.setText( u"<div align='center'> <strong>{}</strong> </div><div align='center' style='margin-top: 3px'> {} </div>".format( self.tr(u"No results."), self.tr(u"You can add a service to become searchable. Start <a href='{}'>here</a>.").format( u"https://qms.nextgis.com/create" ), ) ) new_item = QListWidgetItem(self.lstSearchResult) new_item.setSizeHint(new_widget.sizeHint()) self.lstSearchResult.addItem(new_item) self.lstSearchResult.setItemWidget( new_item, new_widget )
class BarValueConfigWidget(QWidget): """ Widget for editing y-value configurations. """ def __init__(self, parent=None, value_field='', def_color='#2564e1', legend_name=''): QWidget.__init__(self, parent) self._value_field = value_field # Insert controls for editing fill color and legend names self.lbl_fill_color = QLabel(self) self.lbl_fill_color.setText( QApplication.translate("ValueConfigWidget", "Fill color")) self.fill_color_btn = QgsColorButton( self, QApplication.translate("ValueConfigWidget", "Select bar fill color")) self.fill_color_btn.setMaximumHeight(30) self.fill_color_btn.setMinimumHeight(30) self.fill_color_btn.setMinimumWidth(100) if QColor.isValidColor(def_color): default_color = QColor(def_color) self.fill_color_btn.setColor(default_color) self.lbl_legend_name = QLabel(self) self.lbl_legend_name.setText( QApplication.translate("ValueConfigWidget", "Legend name")) self.txt_legend_name = QLineEdit(self) self.txt_legend_name.setMaxLength(50) self.txt_legend_name.setMinimumHeight(30) self.txt_legend_name.setText(legend_name) self.layout = QGridLayout(self) self.layout.addWidget(self.lbl_fill_color, 0, 0, 1, 1) self.layout.addWidget(self.fill_color_btn, 0, 1, 1, 1) self.layout.addWidget(self.lbl_legend_name, 1, 0, 1, 1) self.layout.addWidget(self.txt_legend_name, 1, 1, 1, 1) def value_field(self): """ :return: Returns the value field used from the referenced table. :rtype: str """ return self._value_field def set_value_field(self, value_field): """ Set the value field used from the referenced table. :param value_field: Value field used from the referenced table. :type value_field: str """ if value_field: self._value_field = value_field def configuration(self): """ :return: BarValueConfiguration settings. :rtype: BarValueConfiguration """ from stdm.composer.chart_configuration import BarValueConfiguration bar_value_config = BarValueConfiguration() bar_value_config.set_value_field(self._value_field) bar_value_config.set_fill_color(self.fill_color_btn.color().name()) bar_value_config.set_legend_name(self.txt_legend_name.text()) return bar_value_config def set_configuration(self, config): pass
class OptionsDialog(QDialog, FORM_CLASS): """Options dialog for the InaSAFE plugin.""" def __init__(self, iface, parent=None, qsetting=''): """Constructor for the dialog. :param iface: A Quantum GIS QgisAppInterface instance. :type iface: QgisAppInterface :param parent: Parent widget of this dialog :type parent: QWidget :param qsetting: String to specify the QSettings. By default, use empty string. :type qsetting: str """ QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'configure-inasafe.svg') self.setWindowIcon(QIcon(icon)) self.setWindowTitle(self.tr('InaSAFE %s Options' % get_version())) # Save reference to the QGIS interface and parent self.iface = iface self.parent = parent if qsetting: self.settings = QSettings(qsetting) else: self.settings = QSettings() # InaSAFE default values self.default_value_parameters = [] self.default_value_parameter_containers = [] # Flag for restore default values self.is_restore_default = False # List of setting key and control self.boolean_settings = { 'visibleLayersOnlyFlag': self.cbxVisibleLayersOnly, 'set_layer_from_title_flag': self.cbxSetLayerNameFromTitle, 'setZoomToImpactFlag': self.cbxZoomToImpact, 'set_show_only_impact_on_report': self.cbx_show_only_impact, 'print_atlas_report': self.cbx_print_atlas_report, 'setHideExposureFlag': self.cbxHideExposure, 'useSelectedFeaturesOnly': self.cbxUseSelectedFeaturesOnly, 'useSentry': self.cbxUseSentry, 'template_warning_verbose': self.template_warning_checkbox, 'showOrganisationLogoInDockFlag': self.organisation_on_dock_checkbox, 'developer_mode': self.cbxDevMode, 'generate_report': self.checkbox_generate_reports, 'memory_profile': self.check_box_memory, 'always_show_welcome_message': self.welcome_message_check_box } self.text_settings = { 'keywordCachePath': self.leKeywordCachePath, 'ISO19115_ORGANIZATION': self.organisation_line_edit, 'ISO19115_URL': self.website_line_edit, 'ISO19115_EMAIL': self.email_line_edit, 'ISO19115_LICENSE': self.license_line_edit, } # Export and Import button # Export button self.export_button = QPushButton(tr('Export')) # noinspection PyUnresolvedReferences self.export_button.clicked.connect(self.export_setting) self.button_box.addButton(self.export_button, QDialogButtonBox.ActionRole) # Import button self.import_button = QPushButton(tr('Import')) # noinspection PyUnresolvedReferences self.import_button.clicked.connect(self.import_setting) self.button_box.addButton(self.import_button, QDialogButtonBox.ActionRole) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Always set first tab to be open, 0-th index self.tabWidget.setCurrentIndex(0) # Hide not implemented group self.grpNotImplemented.hide() self.adjustSize() # Population parameter Tab # Label self.preference_label = QLabel() self.preference_label.setText( tr('Please set parameters for each hazard class below. Affected ' 'status and displacement rates selected on this tab are only ' 'applied to exposed populations. ')) self.preference_layout.addWidget(self.preference_label) # Profile preference widget self.profile_widget = ProfileWidget() self.preference_layout.addWidget(self.profile_widget) # Demographic tab self.demographic_label = QLabel() self.demographic_label.setText( tr('Please set the global default demographic ratio below.')) self.default_values_layout.addWidget(self.demographic_label) self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) self.widget_container = QWidget() self.scroll_area.setWidget(self.widget_container) self.container_layout = QVBoxLayout() self.widget_container.setLayout(self.container_layout) self.default_values_layout.addWidget(self.scroll_area) # Restore state from setting self.restore_state() # Hide checkbox if not developers if not self.cbxDevMode.isChecked(): self.checkbox_generate_reports.hide() # Connections # Check boxes self.custom_north_arrow_checkbox.toggled.connect(self.set_north_arrow) self.custom_UseUserDirectory_checkbox.toggled.connect( self.set_user_dir) self.custom_templates_dir_checkbox.toggled.connect( self.set_templates_dir) self.custom_org_disclaimer_checkbox.toggled.connect( self.set_org_disclaimer) self.custom_organisation_logo_check_box.toggled.connect( self.toggle_logo_path) # Buttons self.toolKeywordCachePath.clicked.connect(self.open_keyword_cache_path) self.toolUserDirectoryPath.clicked.connect( self.open_user_directory_path) self.toolNorthArrowPath.clicked.connect(self.open_north_arrow_path) self.open_organisation_logo_path_button.clicked.connect( self.open_organisation_logo_path) self.toolReportTemplatePath.clicked.connect( self.open_report_template_path) # Others self.organisation_logo_path_line_edit.textChanged.connect( self.update_logo_preview) self.earthquake_function.currentIndexChanged.connect( self.update_earthquake_info) # Set up listener for restore defaults button self.demographic_restore_defaults = self.button_box_restore_defaults.\ button(QDialogButtonBox.RestoreDefaults) self.demographic_restore_defaults.setText( self.demographic_restore_defaults.text().capitalize()) self.demographic_restore_defaults.setCheckable(True) self.demographic_restore_defaults.clicked.connect( self.restore_defaults_ratio) # Restore button in population parameter tab self.parameter_population_restore_button = \ self.button_box_restore_preference.button( QDialogButtonBox.RestoreDefaults) self.parameter_population_restore_button.setText( self.parameter_population_restore_button.text().capitalize()) self.parameter_population_restore_button.clicked.connect( partial(self.restore_population_parameters, global_default=True)) # TODO: Hide this until behaviour is defined # hide template warning toggle self.template_warning_checkbox.hide() # hide custom template dir toggle self.custom_templates_dir_checkbox.hide() self.splitter_custom_report.hide() # Welcome message self.set_welcome_message() def save_boolean_setting(self, key, check_box): """Save boolean setting according to check_box state. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ set_setting(key, check_box.isChecked(), qsettings=self.settings) def restore_boolean_setting(self, key, check_box): """Set check_box according to setting of key. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ flag = setting(key, expected_type=bool, qsettings=self.settings) check_box.setChecked(flag) def save_text_setting(self, key, line_edit): """Save text setting according to line_edit value. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ set_setting(key, line_edit.text(), self.settings) def restore_text_setting(self, key, line_edit): """Set line_edit text according to setting of key. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ value = setting(key, expected_type=str, qsettings=self.settings) line_edit.setText(value) def restore_state(self): """Reinstate the options based on the user's stored session info.""" # Restore boolean setting as check box. for key, check_box in list(self.boolean_settings.items()): self.restore_boolean_setting(key, check_box) # Restore text setting as line edit. for key, line_edit in list(self.text_settings.items()): self.restore_text_setting(key, line_edit) # User Directory user_directory_path = setting(key='defaultUserDirectory', default=temp_dir('impacts'), expected_type=str, qsettings=self.settings) custom_user_directory_flag = (user_directory_path != temp_dir('impacts')) self.custom_UseUserDirectory_checkbox.setChecked( custom_user_directory_flag) self.splitter_user_directory.setEnabled(custom_user_directory_flag) self.leUserDirectoryPath.setText(user_directory_path) # Currency # Populate the currency list for currency in currencies: self.currency_combo_box.addItem(currency['name'], currency['key']) # Then make selected the default one. default_currency = setting('currency', expected_type=str) keys = [currency['key'] for currency in currencies] if default_currency not in keys: default_currency = currencies[0]['key'] index = self.currency_combo_box.findData(default_currency) self.currency_combo_box.setCurrentIndex(index) # Earthquake function. # Populate the combobox first. for model in EARTHQUAKE_FUNCTIONS: self.earthquake_function.addItem(model['name'], model['key']) # Then make selected the default one. default_earthquake_function = setting('earthquake_function', expected_type=str) keys = [model['key'] for model in EARTHQUAKE_FUNCTIONS] if default_earthquake_function not in keys: default_earthquake_function = EARTHQUAKE_FUNCTIONS[0]['key'] index = self.earthquake_function.findData(default_earthquake_function) self.earthquake_function.setCurrentIndex(index) self.update_earthquake_info() # Restore North Arrow Image Path north_arrow_path = setting(key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) custom_north_arrow_flag = (north_arrow_path != default_north_arrow_path()) self.custom_north_arrow_checkbox.setChecked(custom_north_arrow_flag) self.splitter_north_arrow.setEnabled(custom_north_arrow_flag) self.leNorthArrowPath.setText(north_arrow_path) # Restore Report Template Directory Path report_template_directory = setting(key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) custom_templates_dir_flag = (report_template_directory != '') self.custom_templates_dir_checkbox.setChecked( custom_templates_dir_flag) self.leReportTemplatePath.setText(report_template_directory) # Restore Disclaimer org_disclaimer = setting(key='reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) custom_org_disclaimer_flag = (org_disclaimer != disclaimer()) self.custom_org_disclaimer_checkbox.setChecked( custom_org_disclaimer_flag) self.txtDisclaimer.setPlainText(org_disclaimer) # Restore Organisation Logo Path org_logo_path = setting(key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) # Check if the path is default one or not custom_org_logo_flag = org_logo_path != supporters_logo_path() self.organisation_logo_path_line_edit.setText(org_logo_path) self.custom_organisation_logo_check_box.setChecked( custom_org_logo_flag) self.organisation_logo_path_line_edit.setEnabled(custom_org_logo_flag) self.open_organisation_logo_path_button.setEnabled( custom_org_logo_flag) # Manually call here self.update_logo_preview() # Restore InaSAFE default values self.restore_default_values_page() # Restore Population Parameter self.restore_population_parameters(global_default=False) def save_state(self): """Store the options into the user's stored session info.""" # Save boolean settings for key, check_box in list(self.boolean_settings.items()): self.save_boolean_setting(key, check_box) # Save text settings for key, line_edit in list(self.text_settings.items()): self.save_text_setting(key, line_edit) set_setting('north_arrow_path', self.leNorthArrowPath.text(), self.settings) set_setting('organisation_logo_path', self.organisation_logo_path_line_edit.text(), self.settings) set_setting('reportTemplatePath', self.leReportTemplatePath.text(), self.settings) set_setting('reportDisclaimer', self.txtDisclaimer.toPlainText(), self.settings) set_setting('defaultUserDirectory', self.leUserDirectoryPath.text(), self.settings) index = self.earthquake_function.currentIndex() value = self.earthquake_function.itemData(index) set_setting('earthquake_function', value, qsettings=self.settings) currency_index = self.currency_combo_box.currentIndex() currency_key = self.currency_combo_box.itemData(currency_index) set_setting('currency', currency_key, qsettings=self.settings) # Save InaSAFE default values self.save_default_values() # Save population parameters self.save_population_parameters() def accept(self): """Method invoked when OK button is clicked.""" self.save_state() super(OptionsDialog, self).accept() def update_earthquake_info(self): """Update information about earthquake info.""" self.label_earthquake_model() current_index = self.earthquake_function.currentIndex() model = EARTHQUAKE_FUNCTIONS[current_index] notes = '' for note in model['notes']: notes += note + '\n\n' citations = '' for citation in model['citations']: citations += citation['text'] + '\n\n' text = tr( 'Description:\n\n%s\n\n' 'Notes:\n\n%s\n\n' 'Citations:\n\n%s') % (model['description'], notes, citations) self.earthquake_fatality_model_notes.setText(text) def label_earthquake_model(self): model = self.earthquake_function.currentText() help_text = tr( 'Please select your preferred earthquake fatality model. The ' 'default fatality model is the {model}.').format(model=model) self.label_default_earthquake.setText(help_text) def open_keyword_cache_path(self): """Open File dialog to choose the keyword cache path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getSaveFileName( self, self.tr('Set keyword cache file'), self.leKeywordCachePath.text(), self.tr('Sqlite DB File (*.db)')) if file_name: self.leKeywordCachePath.setText(file_name) def open_user_directory_path(self): """Open File dialog to choose the user directory path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Results directory'), self.leUserDirectoryPath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leUserDirectoryPath.setText(directory_name) def open_north_arrow_path(self): """Open File dialog to choose the north arrow path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set north arrow image file'), self.leNorthArrowPath.text(), self.tr('Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.leNorthArrowPath.setText(file_name) def open_organisation_logo_path(self): """Open File dialog to choose the organisation logo path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set organisation logo file'), self.organisation_logo_path_line_edit.text(), self.tr('Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.organisation_logo_path_line_edit.setText(file_name) def open_report_template_path(self): """Open File dialog to choose the report template path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Templates directory'), self.leReportTemplatePath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leReportTemplatePath.setText(directory_name) def toggle_logo_path(self): """Set state of logo path line edit and button.""" is_checked = self.custom_organisation_logo_check_box.isChecked() if is_checked: # Use previous org logo path path = setting(key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) else: # Set organisation path line edit to default one path = supporters_logo_path() self.organisation_logo_path_line_edit.setText(path) self.organisation_logo_path_line_edit.setEnabled(is_checked) self.open_organisation_logo_path_button.setEnabled(is_checked) def update_logo_preview(self): """Update logo based on the current logo path.""" logo_path = self.organisation_logo_path_line_edit.text() if os.path.exists(logo_path): icon = QPixmap(logo_path) label_size = self.organisation_logo_label.size() label_size.setHeight(label_size.height() - 2) label_size.setWidth(label_size.width() - 2) scaled_icon = icon.scaled(label_size, Qt.KeepAspectRatio) self.organisation_logo_label.setPixmap(scaled_icon) else: self.organisation_logo_label.setText(tr("Logo not found")) def set_north_arrow(self): """Auto-connect slot activated when north arrow checkbox is toggled.""" is_checked = self.custom_north_arrow_checkbox.isChecked() if is_checked: # Show previous north arrow path path = setting(key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) else: # Set the north arrow line edit to default one path = default_north_arrow_path() self.leNorthArrowPath.setText(path) self.splitter_north_arrow.setEnabled(is_checked) def set_user_dir(self): """Auto-connect slot activated when user dir checkbox is toggled. """ is_checked = self.custom_UseUserDirectory_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting(key='defaultUserDirectory', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = temp_dir('impacts') self.leUserDirectoryPath.setText(path) self.splitter_user_directory.setEnabled(is_checked) def set_templates_dir(self): """Auto-connect slot activated when templates dir checkbox is toggled. """ is_checked = self.custom_templates_dir_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting(key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = '' self.leReportTemplatePath.setText(path) self.splitter_custom_report.setEnabled(is_checked) def set_org_disclaimer(self): """Auto-connect slot activated when org disclaimer checkbox is toggled. """ is_checked = self.custom_org_disclaimer_checkbox.isChecked() if is_checked: # Show previous organisation disclaimer org_disclaimer = setting('reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) else: # Set the organisation disclaimer to the default one org_disclaimer = disclaimer() self.txtDisclaimer.setPlainText(org_disclaimer) self.txtDisclaimer.setEnabled(is_checked) @pyqtSlot(bool) # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = options_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def restore_default_values_page(self): """Setup UI for default values setting.""" # Clear parameters so it doesn't add parameters when # restore from changes. if self.default_value_parameters: self.default_value_parameters = [] if self.default_value_parameter_containers: self.default_value_parameter_containers = [] for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) default_fields = all_default_fields() for field_group in all_field_groups: settable_fields = [] for field in field_group['fields']: if field not in default_fields: continue else: settable_fields.append(field) default_fields.remove(field) if not settable_fields: continue # Create group box for each field group group_box = QGroupBox(self) group_box.setTitle(field_group['name']) self.container_layout.addWidget(group_box) parameters = [] for settable_field in settable_fields: parameter = self.default_field_to_parameter(settable_field) if parameter: parameters.append(parameter) parameter_container = ParameterContainer( parameters, description_text=field_group['description'], extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) group_box_inner_layout = QVBoxLayout() group_box_inner_layout.addWidget(parameter_container) group_box.setLayout(group_box_inner_layout) # Add to attribute self.default_value_parameter_containers.append(parameter_container) # Only show non-groups default fields if there is one if len(default_fields) > 0: for default_field in default_fields: parameter = self.default_field_to_parameter(default_field) if parameter: self.default_value_parameters.append(parameter) description_text = tr( 'In this options you can change the global default values for ' 'these variables.') parameter_container = ParameterContainer( self.default_value_parameters, description_text=description_text, extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) self.other_group_box = QGroupBox(tr('Non-group fields')) other_group_inner_layout = QVBoxLayout() other_group_inner_layout.addWidget(parameter_container) self.other_group_box.setLayout(other_group_inner_layout) self.container_layout.addWidget(self.other_group_box) # Add to attribute self.default_value_parameter_containers.append(parameter_container) def restore_population_parameters(self, global_default=True): """Setup UI for population parameter page from setting. :param global_default: If True, set to original default (from the value in definitions). :type global_default: bool """ if global_default: data = generate_default_profile() else: data = setting('population_preference', generate_default_profile()) if not isinstance(data, dict): LOGGER.debug( 'population parameter is not a dictionary. InaSAFE will use ' 'the default one.') data = generate_default_profile() try: self.profile_widget.data = data except KeyError as e: LOGGER.debug( 'Population parameter is not in correct format. InaSAFE will ' 'use the default one.') LOGGER.debug(e) data = generate_default_profile() self.profile_widget.data = data @staticmethod def age_ratios(): """Helper to get list of age ratio from the options dialog. :returns: List of age ratio. :rtype: list """ # FIXME(IS) set a correct parameter container parameter_container = None youth_ratio = parameter_container.get_parameter_by_guid( youth_ratio_field['key']).value adult_ratio = parameter_container.get_parameter_by_guid( adult_ratio_field['key']).value elderly_ratio = parameter_container.get_parameter_by_guid( elderly_ratio_field['key']).value ratios = [youth_ratio, adult_ratio, elderly_ratio] return ratios def is_good_age_ratios(self): """Method to check the sum of age ratio is 1. :returns: True if the sum is 1 or the sum less than 1 but there is None. :rtype: bool """ ratios = self.age_ratios() if None in ratios: # If there is None, just check to not exceeding 1 clean_ratios = [x for x in ratios if x is not None] ratios.remove(None) if sum(clean_ratios) > 1: return False else: if sum(ratios) != 1: return False return True def save_default_values(self): """Save InaSAFE default values.""" for parameter_container in self.default_value_parameter_containers: parameters = parameter_container.get_parameters() for parameter in parameters: set_inasafe_default_value_qsetting(self.settings, GLOBAL, parameter.guid, parameter.value) def restore_defaults_ratio(self): """Restore InaSAFE default ratio.""" # Set the flag to true because user ask to. self.is_restore_default = True # remove current default ratio for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) # reload default ratio self.restore_default_values_page() def default_field_to_parameter(self, default_field): """Obtain parameter from default field. :param default_field: A default field definition. :type default_field: dict :returns: A parameter object. :rtype: FloatParameter, IntegerParameter """ if default_field.get('type') == QVariant.Double: parameter = FloatParameter() elif default_field.get('type') in qvariant_whole_numbers: parameter = IntegerParameter() else: return default_value = default_field.get('default_value') if not default_value: message = ('InaSAFE default field %s does not have default value' % default_field.get('name')) LOGGER.exception(message) return parameter.guid = default_field.get('key') parameter.name = default_value.get('name') parameter.is_required = True parameter.precision = default_field.get('precision') parameter.minimum_allowed_value = default_value.get('min_value', 0) parameter.maximum_allowed_value = default_value.get( 'max_value', 100000000) parameter.help_text = default_value.get('help_text') parameter.description = default_value.get('description') # Check if user ask to restore to the most default value. if self.is_restore_default: parameter._value = default_value.get('default_value') else: # Current value qsetting_default_value = get_inasafe_default_value_qsetting( self.settings, GLOBAL, default_field['key']) # To avoid python error if qsetting_default_value > parameter.maximum_allowed_value: qsetting_default_value = parameter.maximum_allowed_value if qsetting_default_value < parameter.minimum_allowed_value: qsetting_default_value = parameter.minimum_allowed_value parameter.value = qsetting_default_value return parameter def save_population_parameters(self): """Helper to save population parameter to QSettings.""" population_parameter = self.profile_widget.data set_setting('population_preference', population_parameter) def set_welcome_message(self): """Create and insert welcome message.""" string = html_header() string += welcome_message().to_html() string += html_footer() self.welcome_message.setHtml(string) def show_option_dialog(self): """Helper to show usual option dialog (without welcome message tab).""" self.tabWidget.removeTab(0) def show_welcome_dialog(self): """Setup for showing welcome message dialog. This method will setup several things: - Only show welcome, organisation profile, and population parameter tab. Currently, they are the first 3 tabs. - Set the title - Move the check box for always showing welcome message. """ self.welcome_layout.addWidget(self.welcome_message_check_box) while self.tabWidget.count() > 3: self.tabWidget.removeTab(self.tabWidget.count() - 1) self.setWindowTitle(self.tr('Welcome to InaSAFE %s' % get_version())) # Hide the export import button self.export_button.hide() self.import_button.hide() def export_setting(self): """Export setting from an existing file.""" LOGGER.debug('Export button clicked') home_directory = os.path.expanduser('~') file_name = self.organisation_line_edit.text().replace(' ', '_') file_path, __ = QFileDialog.getSaveFileName( self, self.tr('Export InaSAFE settings'), os.path.join(home_directory, file_name + '.json'), self.tr('JSON File (*.json)')) if file_path: LOGGER.debug('Exporting to %s' % file_path) export_setting(file_path) def import_setting(self): """Import setting to a file.""" LOGGER.debug('Import button clicked') home_directory = os.path.expanduser('~') file_path, __ = QFileDialog.getOpenFileName( self, self.tr('Import InaSAFE settings'), home_directory, self.tr('JSON File (*.json)')) if file_path: title = tr('Import InaSAFE Settings.') question = tr( 'This action will replace your current InaSAFE settings with ' 'the setting from the file. This action is not reversible. ' 'Are you sure to import InaSAFE Setting?') answer = QMessageBox.question(self, title, question, QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.Yes: LOGGER.debug('Import from %s' % file_path) import_setting(file_path)
def construct_form_param_system(self, row, pos): widget = None for field in row[pos]['fields']: if field['label']: lbl = QLabel() lbl.setObjectName('lbl' + field['widgetname']) lbl.setText(field['label']) lbl.setMinimumSize(160, 0) lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) lbl.setToolTip(field['tooltip']) if field['widgettype'] == 'text' or field[ 'widgettype'] == 'linetext': widget = QLineEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'textarea': widget = QTextEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'combo': widget = QComboBox() self.populate_combo(widget, field) widget.currentIndexChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'checkbox' or field[ 'widgettype'] == 'check': widget = QCheckBox() if field['value'] in ('true', 'True', 'TRUE', True): widget.setChecked(True) elif field['value'] in ('false', 'False', 'FALSE', False): widget.setChecked(False) widget.stateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) elif field['widgettype'] == 'datetime': widget = QDateEdit() widget.setCalendarPopup(True) if field['value']: field['value'] = field['value'].replace('/', '-') date = QDate.fromString(field['value'], 'yyyy-MM-dd') widget.setDate(date) widget.dateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'spinbox': widget = QSpinBox() if 'value' in field and field['value'] is not None: value = float(str(field['value'])) widget.setValue(value) widget.valueChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) else: pass if widget: widget.setObjectName(field['widgetname']) else: pass # Order Widgets if 'layoutname' in field: if field['layoutname'] == 'lyt_topology': self.order_widgets_system(field, self.topology_form, lbl, widget) elif field['layoutname'] == 'lyt_builder': self.order_widgets_system(field, self.builder_form, lbl, widget) elif field['layoutname'] == 'lyt_review': self.order_widgets_system(field, self.review_form, lbl, widget) elif field['layoutname'] == 'lyt_analysis': self.order_widgets_system(field, self.analysis_form, lbl, widget) elif field['layoutname'] == 'lyt_system': self.order_widgets_system(field, self.system_form, lbl, widget)
class QgsPluginInstaller(QObject): """ The main class for managing the plugin installer stuff""" statusLabel = None # ----------------------------------------- # def __init__(self): """ Initialize data objects, starts fetching if appropriate, and warn about/removes obsolete plugins """ QObject.__init__(self) # initialize QObject in order to to use self.tr() repositories.load() plugins.getAllInstalled() if repositories.checkingOnStart() and repositories.timeForChecking() and repositories.allEnabled(): # start fetching repositories self.statusLabel = QLabel(self.tr("Looking for new plugins...") + " ", iface.mainWindow().statusBar()) iface.mainWindow().statusBar().insertPermanentWidget(0, self.statusLabel) self.statusLabel.linkActivated.connect(self.showPluginManagerWhenReady) repositories.checkingDone.connect(self.checkingDone) for key in repositories.allEnabled(): repositories.requestFetching(key) else: # no fetching at start, so mark all enabled repositories as requesting to be fetched. for key in repositories.allEnabled(): repositories.setRepositoryData(key, "state", 3) # look for obsolete plugins (the user-installed one is newer than core one) for key in plugins.obsoletePlugins: plugin = plugins.localCache[key] msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle(self.tr("QGIS Python Plugin Installer")) msg.addButton(self.tr("Uninstall (recommended)"), QMessageBox.AcceptRole) msg.addButton(self.tr("I will uninstall it later"), QMessageBox.RejectRole) msg.setText("%s <b>%s</b><br/><br/>%s" % (self.tr("Obsolete plugin:"), plugin["name"], self.tr("QGIS has detected an obsolete plugin that masks its more recent version shipped with this copy of QGIS. This is likely due to files associated with a previous installation of QGIS. Do you want to remove the old plugin right now and unmask the more recent version?"))) msg.exec_() if not msg.result(): # uninstall, update utils and reload if enabled self.uninstallPlugin(key, quiet=True) updateAvailablePlugins() settings = QSettings() if settings.value("/PythonPlugins/" + key, False, type=bool): settings.setValue("/PythonPlugins/watchDog/" + key, True) loadPlugin(key) startPlugin(key) settings.remove("/PythonPlugins/watchDog/" + key) # ----------------------------------------- # def fetchAvailablePlugins(self, reloadMode): """ Fetch plugins from all enabled repositories.""" """ reloadMode = true: Fully refresh data from QSettings to mRepositories """ """ reloadMode = false: Fetch unready repositories only """ QApplication.setOverrideCursor(Qt.WaitCursor) if reloadMode: repositories.load() plugins.clearRepoCache() plugins.getAllInstalled() for key in repositories.allEnabled(): if reloadMode or repositories.all()[key]["state"] == 3: # if state = 3 (error or not fetched yet), try to fetch once again repositories.requestFetching(key) if repositories.fetchingInProgress(): fetchDlg = QgsPluginInstallerFetchingDialog(iface.mainWindow()) fetchDlg.exec_() del fetchDlg for key in repositories.all(): repositories.killConnection(key) QApplication.restoreOverrideCursor() # display error messages for every unavailable reposioty, unless Shift pressed nor all repositories are unavailable keepQuiet = QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier) if repositories.allUnavailable() and repositories.allUnavailable() != repositories.allEnabled(): for key in repositories.allUnavailable(): if not keepQuiet: QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self.tr("Error reading repository:") + " " + key + "\n\n" + repositories.all()[key]["error"]) if QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier): keepQuiet = True # finally, rebuild plugins from the caches plugins.rebuild() # ----------------------------------------- # def checkingDone(self): """ Remove the "Looking for new plugins..." label and display a notification instead if any updates or news available """ if not self.statusLabel: # only proceed if the label is present return # rebuild plugins cache plugins.rebuild() # look for news in the repositories plugins.markNews() status = "" # first check for news for key in plugins.all(): if plugins.all()[key]["status"] == "new": status = self.tr("There is a new plugin available") tabIndex = 4 # PLUGMAN_TAB_NEW # then check for updates (and eventually overwrite status) for key in plugins.all(): if plugins.all()[key]["status"] == "upgradeable": status = self.tr("There is a plugin update available") tabIndex = 3 # PLUGMAN_TAB_UPGRADEABLE # finally set the notify label if status: self.statusLabel.setText(u' <a href="%d">%s</a> ' % (tabIndex, status)) else: iface.mainWindow().statusBar().removeWidget(self.statusLabel) self.statusLabel = None # ----------------------------------------- # def exportRepositoriesToManager(self): """ Update manager's repository tree widget with current data """ iface.pluginManagerInterface().clearRepositoryList() for key in repositories.all(): url = repositories.all()[key]["url"] + repositories.urlParams() if repositories.inspectionFilter(): enabled = (key == repositories.inspectionFilter()) else: enabled = repositories.all()[key]["enabled"] iface.pluginManagerInterface().addToRepositoryList({ "name": key, "url": url, "enabled": enabled and "true" or "false", "valid": repositories.all()[key]["valid"] and "true" or "false", "state": str(repositories.all()[key]["state"]), "error": repositories.all()[key]["error"], "inspection_filter": repositories.inspectionFilter() and "true" or "false" }) # ----------------------------------------- # def exportPluginsToManager(self): """ Insert plugins metadata to QgsMetadataRegistry """ iface.pluginManagerInterface().clearPythonPluginMetadata() for key in plugins.all(): plugin = plugins.all()[key] iface.pluginManagerInterface().addPluginMetadata({ "id": key, "plugin_id": plugin["plugin_id"] or "", "name": plugin["name"], "description": plugin["description"], "about": plugin["about"], "category": plugin["category"], "tags": plugin["tags"], "changelog": plugin["changelog"], "author_name": plugin["author_name"], "author_email": plugin["author_email"], "homepage": plugin["homepage"], "tracker": plugin["tracker"], "code_repository": plugin["code_repository"], "version_installed": plugin["version_installed"], "library": plugin["library"], "icon": plugin["icon"], "readonly": plugin["readonly"] and "true" or "false", "installed": plugin["installed"] and "true" or "false", "available": plugin["available"] and "true" or "false", "status": plugin["status"], "error": plugin["error"], "error_details": plugin["error_details"], "experimental": plugin["experimental"] and "true" or "false", "deprecated": plugin["deprecated"] and "true" or "false", "trusted": plugin["trusted"] and "true" or "false", "version_available": plugin["version_available"], "zip_repository": plugin["zip_repository"], "download_url": plugin["download_url"], "filename": plugin["filename"], "downloads": plugin["downloads"], "average_vote": plugin["average_vote"], "rating_votes": plugin["rating_votes"], "pythonic": "true" }) iface.pluginManagerInterface().reloadModel() # ----------------------------------------- # def reloadAndExportData(self): """ Reload All repositories and export data to the Plugin Manager """ self.fetchAvailablePlugins(reloadMode=True) self.exportRepositoriesToManager() self.exportPluginsToManager() # ----------------------------------------- # def showPluginManagerWhenReady(self, * params): """ Open the plugin manager window. If fetching is still in progress, it shows the progress window first """ """ Optionally pass the index of tab to be opened in params """ if self.statusLabel: iface.mainWindow().statusBar().removeWidget(self.statusLabel) self.statusLabel = None self.fetchAvailablePlugins(reloadMode=False) self.exportRepositoriesToManager() self.exportPluginsToManager() # finally, show the plugin manager window tabIndex = -1 if len(params) == 1: indx = str(params[0]) if indx.isdigit() and int(indx) > -1 and int(indx) < 7: tabIndex = int(indx) iface.pluginManagerInterface().showPluginManager(tabIndex) # ----------------------------------------- # def onManagerClose(self): """ Call this method when closing manager window - it resets last-use-dependent values. """ plugins.updateSeenPluginsList() repositories.saveCheckingOnStartLastDate() # ----------------------------------------- # def exportSettingsGroup(self): """ Return QSettings settingsGroup value """ return settingsGroup # ----------------------------------------- # def upgradeAllUpgradeable(self): """ Reinstall all upgradeable plugins """ for key in plugins.allUpgradeable(): self.installPlugin(key, quiet=True) # ----------------------------------------- # def installPlugin(self, key, quiet=False): """ Install given plugin """ error = False infoString = ('', '') plugin = plugins.all()[key] previousStatus = plugin["status"] if not plugin: return if plugin["status"] == "newer" and not plugin["error"]: # ask for confirmation if user downgrades an usable plugin if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self.tr("Are you sure you want to downgrade the plugin to the latest available version? The installed one is newer!"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return dlg = QgsPluginInstallerInstallingDialog(iface.mainWindow(), plugin) dlg.exec_() if dlg.result(): error = True infoString = (self.tr("Plugin installation failed"), dlg.result()) elif not QDir(qgis.utils.home_plugin_path + "/" + key).exists(): error = True infoString = (self.tr("Plugin has disappeared"), self.tr("The plugin seems to have been installed but I don't know where. Probably the plugin package contained a wrong named directory.\nPlease search the list of installed plugins. I'm nearly sure you'll find the plugin there, but I just can't determine which of them it is. It also means that I won't be able to determine if this plugin is installed and inform you about available updates. However the plugin may work. Please contact the plugin author and submit this issue.")) QApplication.setOverrideCursor(Qt.WaitCursor) plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() QApplication.restoreOverrideCursor() else: QApplication.setOverrideCursor(Qt.WaitCursor) # update the list of plugins in plugin handling routines updateAvailablePlugins() # try to load the plugin loadPlugin(plugin["id"]) plugins.getAllInstalled(testLoad=True) plugins.rebuild() plugin = plugins.all()[key] if not plugin["error"]: if previousStatus in ["not installed", "new"]: infoString = (self.tr("Plugin installed successfully"), "") if startPlugin(plugin["id"]): settings = QSettings() settings.setValue("/PythonPlugins/" + plugin["id"], True) else: settings = QSettings() if settings.value("/PythonPlugins/" + key, False, type=bool): # plugin will be reloaded on the fly only if currently loaded reloadPlugin(key) # unloadPlugin + loadPlugin + startPlugin infoString = (self.tr("Plugin reinstalled successfully"), "") else: unloadPlugin(key) # Just for a case. Will exit quietly if really not loaded loadPlugin(key) infoString = (self.tr("Plugin reinstalled successfully"), self.tr("Python plugin reinstalled.\nYou need to restart QGIS in order to reload it.")) if quiet: infoString = (None, None) QApplication.restoreOverrideCursor() else: QApplication.restoreOverrideCursor() if plugin["error"] == "incompatible": message = self.tr("The plugin is not compatible with this version of QGIS. It's designed for QGIS versions:") message += " <b>" + plugin["error_details"] + "</b>" elif plugin["error"] == "dependent": message = self.tr("The plugin depends on some components missing on your system. You need to install the following Python module in order to enable it:") message += "<b> " + plugin["error_details"] + "</b>" else: message = self.tr("The plugin is broken. Python said:") message += "<br><b>" + plugin["error_details"] + "</b>" dlg = QgsPluginInstallerPluginErrorDialog(iface.mainWindow(), message) dlg.exec_() if dlg.result(): # revert installation pluginDir = qgis.utils.home_plugin_path + "/" + plugin["id"] result = removeDir(pluginDir) if QDir(pluginDir).exists(): error = True infoString = (self.tr("Plugin uninstall failed"), result) try: exec ("sys.path_importer_cache.clear()") exec ("import %s" % plugin["id"]) exec ("reload (%s)" % plugin["id"]) except: pass else: try: exec ("del sys.modules[%s]" % plugin["id"]) except: pass plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() if infoString[0]: level = error and QgsMessageBar.CRITICAL or QgsMessageBar.INFO msg = "<b>%s:</b>%s" % (infoString[0], infoString[1]) iface.pluginManagerInterface().pushMessage(msg, level) # ----------------------------------------- # def uninstallPlugin(self, key, quiet=False): """ Uninstall given plugin """ if key in plugins.all(): plugin = plugins.all()[key] else: plugin = plugins.localCache[key] if not plugin: return if not quiet: warning = self.tr("Are you sure you want to uninstall the following plugin?") + "\n(" + plugin["name"] + ")" if plugin["status"] == "orphan" and not plugin["error"]: warning += "\n\n" + self.tr("Warning: this plugin isn't available in any accessible repository!") if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # unload the plugin QApplication.setOverrideCursor(Qt.WaitCursor) try: unloadPlugin(key) except: pass pluginDir = qgis.utils.home_plugin_path + "/" + plugin["id"] result = removeDir(pluginDir) if result: QApplication.restoreOverrideCursor() msg = "<b>%s:</b>%s" % (self.tr("Plugin uninstall failed"), result) iface.pluginManagerInterface().pushMessage(msg, QgsMessageBar.CRITICAL) else: # safe remove try: unloadPlugin(plugin["id"]) except: pass try: exec ("plugins[%s].unload()" % plugin["id"]) exec ("del plugins[%s]" % plugin["id"]) except: pass try: exec ("del sys.modules[%s]" % plugin["id"]) except: pass plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() QApplication.restoreOverrideCursor() iface.pluginManagerInterface().pushMessage(self.tr("Plugin uninstalled successfully"), QgsMessageBar.INFO) # ----------------------------------------- # def addRepository(self): """ add new repository connection """ dlg = QgsPluginInstallerRepositoryDialog(iface.mainWindow()) dlg.editParams.setText(repositories.urlParams()) dlg.checkBoxEnabled.setCheckState(Qt.Checked) if not dlg.exec_(): return for i in list(repositories.all().values()): if dlg.editURL.text().strip() == i["url"]: iface.pluginManagerInterface().pushMessage(self.tr("Unable to add another repository with the same URL!"), QgsMessageBar.WARNING) return settings = QSettings() settings.beginGroup(reposGroup) reposName = dlg.editName.text() reposURL = dlg.editURL.text().strip() if reposName in repositories.all(): reposName = reposName + "(2)" # add to settings settings.setValue(reposName + "/url", reposURL) settings.setValue(reposName + "/authcfg", dlg.editAuthCfg.text().strip()) settings.setValue(reposName + "/enabled", bool(dlg.checkBoxEnabled.checkState())) # refresh lists and populate widgets plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def editRepository(self, reposName): """ edit repository connection """ if not reposName: return reposName = reposName.decode('utf-8') checkState = {False: Qt.Unchecked, True: Qt.Checked} dlg = QgsPluginInstallerRepositoryDialog(iface.mainWindow()) dlg.editName.setText(reposName) dlg.editURL.setText(repositories.all()[reposName]["url"]) dlg.editAuthCfg.setText(repositories.all()[reposName]["authcfg"]) dlg.editParams.setText(repositories.urlParams()) dlg.checkBoxEnabled.setCheckState(checkState[repositories.all()[reposName]["enabled"]]) if repositories.all()[reposName]["valid"]: dlg.checkBoxEnabled.setEnabled(True) dlg.labelInfo.setText("") else: dlg.checkBoxEnabled.setEnabled(False) dlg.labelInfo.setText(self.tr("This repository is blocked due to incompatibility with your QGIS version")) dlg.labelInfo.setFrameShape(QFrame.Box) if not dlg.exec_(): return # nothing to do if cancelled for i in list(repositories.all().values()): if dlg.editURL.text().strip() == i["url"] and dlg.editURL.text().strip() != repositories.all()[reposName]["url"]: iface.pluginManagerInterface().pushMessage(self.tr("Unable to add another repository with the same URL!"), QgsMessageBar.WARNING) return # delete old repo from QSettings and create new one settings = QSettings() settings.beginGroup(reposGroup) settings.remove(reposName) newName = dlg.editName.text() if newName in repositories.all() and newName != reposName: newName = newName + "(2)" settings.setValue(newName + "/url", dlg.editURL.text().strip()) settings.setValue(newName + "/authcfg", dlg.editAuthCfg.text().strip()) settings.setValue(newName + "/enabled", bool(dlg.checkBoxEnabled.checkState())) if dlg.editAuthCfg.text().strip() != repositories.all()[reposName]["authcfg"]: repositories.all()[reposName]["authcfg"] = dlg.editAuthCfg.text().strip() if dlg.editURL.text().strip() == repositories.all()[reposName]["url"] and dlg.checkBoxEnabled.checkState() == checkState[repositories.all()[reposName]["enabled"]]: repositories.rename(reposName, newName) self.exportRepositoriesToManager() return # nothing else to do if only repository name was changed plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def deleteRepository(self, reposName): """ delete repository connection """ if not reposName: return reposName = reposName.decode('utf-8') settings = QSettings() settings.beginGroup(reposGroup) if settings.value(reposName + "/url", "", type=str) == officialRepo[1]: iface.pluginManagerInterface().pushMessage(self.tr("You can't remove the official QGIS Plugin Repository. You can disable it if needed."), QgsMessageBar.WARNING) return warning = self.tr("Are you sure you want to remove the following repository?") + "\n" + reposName if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # delete from the settings, refresh data and repopulate all the widgets settings.remove(reposName) repositories.remove(reposName) plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def setRepositoryInspectionFilter(self, reposName=None): """ temporarily block another repositories to fetch only one for inspection """ if reposName is not None: reposName = reposName.decode("utf-8") repositories.setInspectionFilter(reposName) self.reloadAndExportData() # ----------------------------------------- # def sendVote(self, plugin_id, vote): """ send vote via the RPC """ if not plugin_id or not vote: return False url = "http://plugins.qgis.org/plugins/RPC2/" params = "{\"id\":\"djangorpc\",\"method\":\"plugin.vote\",\"params\":[%s,%s]}" % (str(plugin_id), str(vote)) req = QNetworkRequest(QUrl(url)) req.setRawHeader("Content-Type", "application/json") QgsNetworkAccessManager.instance().post(req, params) return True
class CommanderWindow(QDialog): def __init__(self, parent, canvas): self.canvas = canvas QDialog.__init__(self, parent, Qt.FramelessWindowHint) self.commands = imp.load_source('commands', self.commandsFile()) self.initGui() def commandsFolder(self): folder = str(os.path.join(userFolder(), 'commander')) mkdir(folder) return os.path.abspath(folder) def commandsFile(self): f = os.path.join(self.commandsFolder(), 'commands.py') if not os.path.exists(f): with open(f, 'w') as out: out.write('from qgis.core import *\n') out.write('import processing\n\n') out.write('def removeall():\n') out.write('\tmapreg = QgsProject.instance()\n') out.write('\tmapreg.removeAllMapLayers()\n\n') out.write('def load(*args):\n') out.write('\tprocessing.load(args[0])\n') return f def algsListHasChanged(self): self.fillCombo() def initGui(self): self.combo = ExtendedComboBox() self.fillCombo() self.combo.setEditable(True) self.label = QLabel('Enter command:') self.errorLabel = QLabel('Enter command:') self.vlayout = QVBoxLayout() self.vlayout.setSpacing(2) self.vlayout.setMargin(0) self.vlayout.addSpacerItem(QSpacerItem(0, OFFSET, QSizePolicy.Maximum, QSizePolicy.Expanding)) self.hlayout = QHBoxLayout() self.hlayout.addWidget(self.label) self.vlayout.addLayout(self.hlayout) self.hlayout2 = QHBoxLayout() self.hlayout2.addWidget(self.combo) self.vlayout.addLayout(self.hlayout2) self.vlayout.addSpacerItem(QSpacerItem(0, OFFSET, QSizePolicy.Maximum, QSizePolicy.Expanding)) self.setLayout(self.vlayout) self.combo.lineEdit().returnPressed.connect(self.run) self.prepareGui() def fillCombo(self): self.combo.clear() # Add algorithms for provider in list(algList.algs.values()): for alg in provider: self.combo.addItem('Processing algorithm: ' + alg) # Add functions for command in dir(self.commands): if isinstance(self.commands.__dict__.get(command), types.FunctionType): self.combo.addItem('Command: ' + command) # Add menu entries menuActions = [] actions = iface.mainWindow().menuBar().actions() for action in actions: menuActions.extend(self.getActions(action)) for action in menuActions: self.combo.addItem('Menu action: ' + str(action.text())) def prepareGui(self): self.combo.setEditText('') self.combo.setMaximumSize(QSize(self.canvas.rect().width() - 2 * OFFSET, ITEMHEIGHT)) self.combo.view().setStyleSheet('min-height: 150px') self.combo.setFocus(Qt.OtherFocusReason) self.label.setMaximumSize(self.combo.maximumSize()) self.label.setVisible(False) self.adjustSize() pt = self.canvas.rect().topLeft() absolutePt = self.canvas.mapToGlobal(pt) self.move(absolutePt) self.resize(self.canvas.rect().width(), HEIGHT) self.setStyleSheet('CommanderWindow {background-color: #e7f5fe; \ border: 1px solid #b9cfe4;}') def getActions(self, action): menuActions = [] menu = action.menu() if menu is None: menuActions.append(action) return menuActions else: actions = menu.actions() for subaction in actions: if subaction.menu() is not None: menuActions.extend(self.getActions(subaction)) elif not subaction.isSeparator(): menuActions.append(subaction) return menuActions def run(self): s = str(self.combo.currentText()) if s.startswith('Processing algorithm: '): algName = s[len('Processing algorithm: '):] alg = algList.getAlgorithm(algName) if alg is not None: self.close() self.runAlgorithm(alg) elif s.startswith("Command: "): command = s[len("Command: "):] try: self.runCommand(command) self.close() except Exception as e: self.label.setVisible(True) self.label.setText('Error:' + str(e)) elif s.startswith('Menu action: '): actionName = s[len('Menu action: '):] menuActions = [] actions = iface.mainWindow().menuBar().actions() for action in actions: menuActions.extend(self.getActions(action)) for action in menuActions: if action.text() == actionName: self.close() action.trigger() return else: try: self.runCommand(s) self.close() except Exception as e: self.label.setVisible(True) self.label.setText('Error:' + str(e)) def runCommand(self, command): tokens = command.split(' ') if len(tokens) == 1: method = self.commands.__dict__.get(command) if method is not None: method() else: raise Exception('Wrong command') else: method = self.commands.__dict__.get(tokens[0]) if method is not None: method(*tokens[1:]) else: raise Exception('Wrong command') def runAlgorithm(self, alg): alg = alg.getCopy() message = alg.checkBeforeOpeningParametersDialog() if message: dlg = MessageDialog() dlg.setTitle(self.tr('Missing dependency')) dlg.setMessage(message) dlg.exec_() return dlg = alg.getCustomParametersDialog() if not dlg: dlg = AlgorithmDialog(alg) canvas = iface.mapCanvas() prevMapTool = canvas.mapTool() dlg.show() dlg.exec_() if canvas.mapTool() != prevMapTool: try: canvas.mapTool().reset() except: pass canvas.setMapTool(prevMapTool)
class AttributesDock(QgsDockWidget): layerChanged = pyqtSignal(QgsMapLayer) currentValueChanged = pyqtSignal(str, QVariant) def __init__(self, iface): QgsDockWidget.__init__( self, QCoreApplication.translate('AttributesDock', 'Quick Attribution')) self.iface = iface self.widget = QWidget() self.widget.setLayout(QGridLayout()) self.widget.setContentsMargins(0, 0, 0, 0) self.layerComboBox = QgsMapLayerComboBox() self.layerComboBox.layerChanged.connect(self.setLayer) self.layerComboBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.layerTitleLabel = QLabel() self.widget.layout().addWidget(self.layerTitleLabel, 0, 0, 1, 1) self.widget.layout().addWidget(self.layerComboBox, 0, 1, 1, 1) self.formWidget = QWidget() self.formWidget.setLayout(QGridLayout()) self.formWidget.setContentsMargins(0, 0, 0, 0) self.widget.layout().addWidget(self.formWidget, 1, 0, 1, 2) self.setWidget(self.widget) self.attributeForm = None self.layer = None self.feature = None self.layerComboBox.setFilters(QgsMapLayerProxyModel.WritableLayer | QgsMapLayerProxyModel.VectorLayer) QgsProject.instance().readProject.connect(self.onProjectRead) def setLayer(self, layer): if layer == self.layer: return if self.layer: try: self.layer.destroyed.disconnect(self.onLayerRemoved) except TypeError: # Do not care if it is not connected pass self.layer = layer if self.layer: self.layer.destroyed.connect(self.onLayerRemoved) self.layerComboBox.setLayer(layer) if self.attributeForm: try: self.attributeForm.deleteLater() except RuntimeError: # Sometimes the form has already been deleted, that's ok for us pass if self.layer is not None: context = QgsAttributeEditorContext() context.setVectorLayerTools(self.iface.vectorLayerTools()) context.setFormMode(QgsAttributeEditorContext.StandaloneDialog) self.attributeForm = QgsAttributeForm(self.layer, QgsFeature(), context) self.attributeForm.hideButtonBox() try: self.layer.updatedFields.disconnect( self.attributeForm.onUpdatedFields) except TypeError: pass fields = self.layer.fields() self.feature = QgsFeature(fields) for idx in range(self.layer.fields().count()): self.feature.setAttribute(idx, self.layer.defaultValue(idx)) self.feature.setValid(True) self.attributeForm.setFeature(self.feature) self.attributeForm.widgetValueChanged.connect( self.onAttributeChanged) self.formWidget.layout().addWidget(self.attributeForm) self.layerChanged.emit(self.layer) def onLayerRemoved(self): self.setLayer(None) def onAttributeChanged(self, attributeName, value, changed): idx = self.layer.fields().indexOf(attributeName) if value != 'NULL': defaultValue = QgsExpression.quotedValue(value) else: defaultValue = 'NULL' self.layer.blockSignals(True) self.layer.setDefaultValueDefinition(idx, QgsDefaultValue(defaultValue)) self.layer.blockSignals(False) self.currentValueChanged.emit(attributeName, value) def onProjectRead(self, doc): title, isDefined = QgsProject.instance().readEntry( 'quick_attribution', 'layercbxtitle') if isDefined: self.layerTitleLabel.setText(title) else: self.layerTitleLabel.setText(self.tr('Layer'))
class QmsSearchResultItemWidget(QWidget): def __init__(self, geoservice, image_ba, parent=None, extent_renderer=None): QWidget.__init__(self, parent) self.extent_renderer = extent_renderer self.layout = QHBoxLayout(self) self.layout.setContentsMargins(5, 10, 5, 10) self.setLayout(self.layout) self.service_icon = QLabel(self) self.service_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.service_icon.resize(24, 24) qimg = QImage.fromData(image_ba) pixmap = QPixmap.fromImage(qimg) self.service_icon.setPixmap(pixmap) self.layout.addWidget(self.service_icon) self.service_desc_layout = QGridLayout(self) self.service_desc_layout.setSpacing(0) self.layout.addLayout(self.service_desc_layout) self.service_name = QLabel(self) self.service_name.setTextFormat(Qt.RichText) self.service_name.setWordWrap(True) self.service_name.setText(u" <strong> {} </strong>".format(geoservice.get('name', u""))) self.service_desc_layout.addWidget(self.service_name, 0, 0, 1, 3) self.service_type = QLabel(self) self.service_type.setTextFormat(Qt.RichText) self.service_type.setWordWrap(True) self.service_type.setText(geoservice.get('type', u"").upper() + " ") self.service_desc_layout.addWidget(self.service_type, 1, 0) self.service_deteils = QLabel(self) self.service_deteils.setTextFormat(Qt.RichText) self.service_deteils.setWordWrap(True) self.service_deteils.setOpenExternalLinks(True) self.service_deteils.setText(u"<a href=\"{0}\">{1}</a>, ".format( Client().geoservice_info_url(geoservice.get('id', u"")), self.tr('details') )) self.service_desc_layout.addWidget(self.service_deteils, 1, 1) self.service_report = QLabel(self) self.service_report.setTextFormat(Qt.RichText) self.service_report.setWordWrap(True) self.service_report.setOpenExternalLinks(True) self.service_report.setText(u"<a href=\"{0}\">{1}</a><div/>".format( Client().geoservice_report_url(geoservice.get('id', u"")), self.tr('report a problem') )) self.service_desc_layout.addWidget(self.service_report, 1, 2) self.service_desc_layout.setColumnStretch(2, 1) self.status_label = QLabel(self) self.status_label.setTextFormat(Qt.RichText) self.status_label.setText(u'\u2022') status = geoservice.get('cumulative_status', u'') if status == 'works': self.status_label.setStyleSheet("color: green; font-size: 30px") if status == 'failed': self.status_label.setStyleSheet("color: red; font-size: 30px") if status == 'problematic': self.status_label.setStyleSheet("color: yellow; font-size: 30px") self.layout.addWidget(self.status_label) self.addButton = QToolButton() self.addButton.setText(self.tr("Add")) self.addButton.clicked.connect(self.addToMap) self.layout.addWidget(self.addButton) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) self.geoservice = geoservice self.image_ba = image_ba def addToMap(self): try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) client = Client() client.set_proxy(*QGISSettings.get_qgis_proxy()) geoservice_info = client.get_geoservice_info(self.geoservice) ds = DataSourceSerializer.read_from_json(geoservice_info) add_layer_to_map(ds) CachedServices().add_service(self.geoservice, self.image_ba) except Exception as ex: plPrint(unicode(ex)) pass finally: QApplication.restoreOverrideCursor() def mouseDoubleClickEvent(self, event): self.addToMap() def enterEvent(self, event): extent = self.geoservice.get('extent', None) if self.extent_renderer and extent: if ';' in extent: extent = extent.split(';')[1] geom = QgsGeometry.fromWkt(extent) self.extent_renderer.show_feature(geom) def leaveEvent(self, event): if self.extent_renderer: self.extent_renderer.clear_feature()
class AdvancedSearch(EntityEditorDialog): search_triggered = pyqtSignal(dict) def __init__(self, entity, parent, initial_values: dict = None): super().__init__(entity, parent=parent) self.parent = parent self.initial_values = initial_values or {} for k, v in self.initial_values.items(): for mapper in self._attrMappers: if mapper.attributeName() == k: mapper.valueHandler().setValue(v) def _init_gui(self, show_str_tab: bool, is_party_unit: bool=False): # Setup base elements self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName('glMain') self.gridLayout.addLayout( self.vlNotification, 0, 0, 1, 1 ) column_widget_area = self._setup_columns_content_area() self.gridLayout.addWidget( column_widget_area, 1, 0, 1, 1 ) # Add notification for mandatory columns if applicable next_row = 2 # Set title search_trans = self.tr('Advanced Search') if self._entity.label is not None: if self._entity.label != '': title_str = self._entity.label else: title_str = format_name(self._entity.short_name) else: title_str = format_name(self._entity.short_name) title = '{0} {1}'.format(title_str, search_trans) self.do_not_check_dirty = True self.setWindowTitle(title) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setObjectName('buttonBox') self.gridLayout.addWidget( self.buttonBox, next_row, 0, 1, 1 ) self.buttonBox.setOrientation(Qt.Horizontal) self.search = QPushButton( QApplication.translate( 'EntityEditorDialog', 'Search' ) ) self.buttonBox.addButton( self.search, QDialogButtonBox.ActionRole ) self.buttonBox.setStandardButtons( QDialogButtonBox.Close | QDialogButtonBox.Reset ) self.search.clicked.connect(self.on_search) self.buttonBox.button(QDialogButtonBox.Close).clicked.connect(self.reject) self.buttonBox.button(QDialogButtonBox.Reset).clicked.connect(self.reset) def reset(self): """ Resets the dialog back to an empty/no filter status """ for column in self._entity.columns.values(): if column.name in entity_display_columns(self._entity): if column.name == 'id': continue handler = self.attribute_mappers[ column.name].valueHandler() handler.setValue(handler.default()) def on_search(self): """ Builds a dictionary of the desired search values and emits the search_triggered signal """ self.search_triggered.emit(self.current_search_data()) def current_search_data(self) -> dict: """ Returns a dictionary representing the current search data """ search_data = {} for column in self._entity.columns.values(): if column.name in entity_display_columns(self._entity): if column.name == 'id': continue handler = self.attribute_mappers[ column.name].valueHandler() value = handler.value() if value != handler.default() and bool(value): search_data[column.name] = value return search_data def _setup_columns_content_area(self): # Only use this if entity supports documents # self.entity_tab_widget = None self.doc_widget = None self.entity_scroll_area = QScrollArea(self) self.entity_scroll_area.setFrameShape(QFrame.NoFrame) self.entity_scroll_area.setWidgetResizable(True) self.entity_scroll_area.setObjectName('scrollArea') # Grid layout for controls self.gl = QGridLayout(self.scroll_widget_contents) self.gl.setObjectName('gl_widget_contents') # Append column labels and widgets table_name = self._entity.name columns = table_column_names(table_name) # Iterate entity column and assert if they exist row_id = 0 for column_name, column_widget in self.column_widgets.items(): c = self.columns[column_name] if c.name in self.exclude_columns: continue if isinstance(c, MultipleSelectColumn): continue if c.name not in columns and not isinstance(c, VirtualColumn): continue if column_widget is not None: header = c.ui_display() self.c_label = QLabel(self.scroll_widget_contents) self.c_label.setText(header) self.gl.addWidget(self.c_label, row_id, 0, 1, 1) if c.TYPE_INFO == 'AUTO_GENERATED': column_widget.setReadOnly(False) column_widget.btn_load.hide() self.gl.addWidget(column_widget, row_id, 1, 1, 1) col_name = c.name # Add widget to MapperMixin collection self.addMapping( col_name, column_widget, c.mandatory, pseudoname=c.ui_display() ) # Bump up row_id row_id += 1 self.entity_scroll_area.setWidget(self.scroll_widget_contents) if self.entity_tab_widget is None: self.entity_tab_widget = QTabWidget(self) # Check if there are children and add foreign key browsers # Add primary tab if necessary self._add_primary_attr_widget() # self.entity_tab_widget.setTabEnabled(0, False) # enable/disable the tab # set the style sheet self.setStyleSheet( "QTabBar::tab::selected {width: 0; height: 0; margin: 0; " "padding: 0; border: none;} ") # Return the correct widget if self.entity_tab_widget is not None: return self.entity_tab_widget return self.entity_scroll_area
class FieldMappingDialog(QDialog, FORM_CLASS): """Dialog implementation class for the InaSAFE field mapping tool.""" def __init__(self, parent=None, iface=None, setting=None): """Constructor.""" QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(self.tr('InaSAFE Field Mapping Tool')) icon = resources_path('img', 'icons', 'show-mapping-tool.svg') self.setWindowIcon(QIcon(icon)) self.parent = parent self.iface = iface if setting is None: setting = QSettings() self.setting = setting self.keyword_io = KeywordIO() self.layer = None self.metadata = {} self.layer_input_layout = QHBoxLayout() self.layer_label = QLabel(tr('Layer')) self.layer_combo_box = QgsMapLayerComboBox() # Filter only for Polygon and Point self.layer_combo_box.setFilters( QgsMapLayerProxyModel.PolygonLayer | QgsMapLayerProxyModel.PointLayer) # Filter out a layer that don't have layer groups excepted_layers = [] for i in range(self.layer_combo_box.count()): layer = self.layer_combo_box.layer(i) try: keywords = self.keyword_io.read_keywords(layer) except (KeywordNotFoundError, NoKeywordsFoundError): excepted_layers.append(layer) continue layer_purpose = keywords.get('layer_purpose') if not layer_purpose: excepted_layers.append(layer) continue if layer_purpose == layer_purpose_exposure['key']: layer_subcategory = keywords.get('exposure') elif layer_purpose == layer_purpose_hazard['key']: layer_subcategory = keywords.get('hazard') else: layer_subcategory = None field_groups = get_field_groups(layer_purpose, layer_subcategory) if len(field_groups) == 0: excepted_layers.append(layer) continue self.layer_combo_box.setExceptedLayerList(excepted_layers) # Select the active layer. if self.iface.activeLayer(): found = self.layer_combo_box.findText( self.iface.activeLayer().name()) if found > -1: self.layer_combo_box.setLayer(self.iface.activeLayer()) self.field_mapping_widget = None self.main_stacked_widget.setCurrentIndex(1) # Input self.layer_input_layout.addWidget(self.layer_label) self.layer_input_layout.addWidget(self.layer_combo_box) self.header_label = QLabel() self.header_label.setWordWrap(True) self.main_layout.addWidget(self.header_label) self.main_layout.addLayout(self.layer_input_layout) # Signal self.layer_combo_box.layerChanged.connect(self.set_layer) if self.layer_combo_box.currentLayer(): self.set_layer(self.layer_combo_box.currentLayer()) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) # Set up things for ok button self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) def set_layer(self, layer=None, keywords=None): """Set layer and update UI accordingly. :param layer: A QgsVectorLayer. :type layer: QgsVectorLayer :param keywords: Keywords for the layer. :type keywords: dict, None """ if self.field_mapping_widget is not None: self.field_mapping_widget.setParent(None) self.field_mapping_widget.close() self.field_mapping_widget.deleteLater() self.main_layout.removeWidget(self.field_mapping_widget) self.field_mapping_widget = None if layer: self.layer = layer else: self.layer = self.layer_combo_box.currentLayer() if not self.layer: return if keywords is not None: self.metadata = keywords else: # Always read from metadata file. try: self.metadata = self.keyword_io.read_keywords(self.layer) except ( NoKeywordsFoundError, KeywordNotFoundError, MetadataReadError) as e: raise e if 'inasafe_default_values' not in self.metadata: self.metadata['inasafe_default_values'] = {} if 'inasafe_fields' not in self.metadata: self.metadata['inasafe_fields'] = {} self.field_mapping_widget = FieldMappingWidget( parent=self, iface=self.iface) self.field_mapping_widget.set_layer(self.layer, self.metadata) self.field_mapping_widget.show() self.main_layout.addWidget(self.field_mapping_widget) # Set header label group_names = [ self.field_mapping_widget.tabText(i) for i in range( self.field_mapping_widget.count())] if len(group_names) == 0: header_text = tr( 'There is no field group for this layer. Please select ' 'another layer.') self.header_label.setText(header_text) return elif len(group_names) == 1: pretty_group_name = group_names[0] elif len(group_names) == 2: pretty_group_name = group_names[0] + tr(' and ') + group_names[1] else: pretty_group_name = ', '.join(group_names[:-1]) pretty_group_name += tr(', and {0}').format(group_names[-1]) header_text = tr( 'Please fill the information for every tab to determine the ' 'attribute for {0} group.').format(pretty_group_name) self.header_label.setText(header_text) @pyqtSlot(bool) # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = field_mapping_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def save_metadata(self): """Save metadata based on the field mapping state.""" metadata = self.field_mapping_widget.get_field_mapping() for key, value in list(metadata['fields'].items()): # Delete the key if it's set to None if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) if value is None or value == []: if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) else: self.metadata['inasafe_fields'][key] = value for key, value in list(metadata['values'].items()): # Delete the key if it's set to None if key in self.metadata['inasafe_fields']: self.metadata['inasafe_fields'].pop(key) if value is None: if key in self.metadata['inasafe_default_values']: self.metadata['inasafe_default_values'].pop(key) else: self.metadata['inasafe_default_values'][key] = value # Save metadata try: self.keyword_io.write_keywords( layer=self.layer, keywords=self.metadata) except InaSAFEError as e: error_message = get_error_message(e) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QMessageBox.warning( self, self.tr('InaSAFE'), ((self.tr( 'An error was encountered when saving the following ' 'keywords:\n %s') % error_message.to_html()))) # Update setting fir recent value if self.metadata.get('inasafe_default_values'): for key, value in \ list(self.metadata['inasafe_default_values'].items()): set_inasafe_default_value_qsetting( self.setting, key, RECENT, value) def accept(self): """Method invoked when OK button is clicked.""" try: self.save_metadata() except InvalidValidationException as e: display_warning_message_box( self, tr('Invalid Field Mapping'), str(e)) return super(FieldMappingDialog, self).accept()
class ProgressDialog(QDialog): """ Progress dialog shows progress bar for algorithm. """ def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.workerThread = None self.state = False self.outputLoc = None self.resultStatus = None self.reRun = False self.savedProj = None # Build GUI Elements self.setWindowTitle("SEILAPLAN wird ausgeführt") self.resize(500, 100) self.container = QVBoxLayout() self.progressBar = QProgressBar(self) self.progressBar.setMinimumWidth(500) self.statusLabel = QLabel(self) self.hbox = QHBoxLayout() self.cancelButton = QDialogButtonBox() self.closeButton = QDialogButtonBox() self.resultLabel = ClickLabel(self) self.resultLabel.setMaximumWidth(500) self.resultLabel.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)) self.resultLabel.setWordWrap(True) self.rerunButton = QPushButton("Berechnungen wiederholen") self.rerunButton.setVisible(False) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.cancelButton.setStandardButtons(QDialogButtonBox.Cancel) self.cancelButton.clicked.connect(self.onAbort) self.closeButton.setStandardButtons(QDialogButtonBox.Close) self.closeButton.clicked.connect(self.onClose) self.hbox.addWidget(self.rerunButton) self.hbox.addItem(spacer) self.hbox.addWidget(self.cancelButton) self.hbox.setAlignment(self.cancelButton, Qt.AlignHCenter) self.hbox.addWidget(self.closeButton) self.hbox.setAlignment(self.closeButton, Qt.AlignHCenter) self.closeButton.hide() self.container.addWidget(self.progressBar) self.container.addWidget(self.statusLabel) self.container.addWidget(self.resultLabel) self.container.addLayout(self.hbox) self.container.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.container) def setThread(self, workerThread): self.workerThread = workerThread self.connectProgressSignals() def connectProgressSignals(self): # Connet signals of thread self.workerThread.sig_jobEnded.connect(self.jobEnded) self.workerThread.sig_jobError.connect(self.onError) self.workerThread.sig_value.connect(self.valueFromThread) self.workerThread.sig_range.connect(self.rangeFromThread) self.workerThread.sig_text.connect(self.textFromThread) self.workerThread.sig_result.connect(self.resultFromThread) self.rerunButton.clicked.connect(self.onRerun) def run(self): # Show modal dialog window (QGIS is still responsive) self.show() # start event loop self.exec_() def jobEnded(self, success): self.setWindowTitle("SEILAPLAN") if success: self.statusLabel.setText("Berechnungen abgeschlossen.") self.progressBar.setValue(self.progressBar.maximum()) self.setFinalMessage() else: # If there was an abort by the user self.statusLabel.setText("Berechnungen abgebrochen.") self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() self.rerunButton.setVisible(True) def valueFromThread(self, value): self.progressBar.setValue(value) def rangeFromThread(self, range_vals): self.progressBar.setRange(range_vals[0], range_vals[1]) def maxFromThread(self, max): self.progressBar.setValue(self.progressBar.maximum()) def textFromThread(self, value): self.statusLabel.setText(value) def resultFromThread(self, result): [self.outputLoc, self.resultStatus] = result def setFinalMessage(self): self.resultLabel.clicked.connect(self.onResultClicked) self.resultLabel.blockSignals(True) linkToFolder = ('<html><head/><body><p></p><p><a href=' '"file:////{0}"><span style="text-decoration: ' 'underline; color:#0000ff;">{0}</span></a></p>' '</body></html>'.format(self.outputLoc)) # Optimization successful if self.resultStatus == 1: self.resultLabel.setText(textOK + linkToFolder) self.resultLabel.blockSignals(False) # Cable takes off of support elif self.resultStatus == 2: self.resultLabel.setText(textSeil + linkToFolder) self.resultLabel.blockSignals(False) # Optimization partially successful elif self.resultStatus == 3: self.resultLabel.setText(textHalf + linkToFolder) self.resultLabel.blockSignals(False) # Optimization not successful elif self.resultStatus == 4: self.resultLabel.setText(textBad) self.setLayout(self.container) def onResultClicked(self): # Open a folder window if sys.platform == 'darwin': # MAC subprocess.call(["open", "-R", self.outputLoc]) elif sys.platform.startswith('linux'): # LINUX subprocess.Popen(["xdg-open", self.outputLoc]) elif 'win32' in sys.platform: # WINDOWS from subprocess import CalledProcessError try: subprocess.check_call(['explorer', self.outputLoc]) except CalledProcessError: pass def onAbort(self): self.setWindowTitle("SEILAPLAN") self.statusLabel.setText("Laufender Prozess wird abgebrochen...") self.workerThread.cancel() # Terminates process cleanly def onError(self, exception_string): self.setWindowTitle("SEILAPLAN: Berechnung fehlgeschlagen") self.statusLabel.setText("Ein Fehler ist aufgetreten:") self.resultLabel.setText(exception_string) self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() def onRerun(self): self.reRun = True self.onClose() def finallyDo(self): self.cancelButton.hide() self.closeButton.show() def onClose(self): self.close()
class QgsPluginInstaller(QObject): """ The main class for managing the plugin installer stuff""" statusLabel = None # ----------------------------------------- # def __init__(self): """ Initialize data objects, starts fetching if appropriate, and warn about/removes obsolete plugins """ QObject.__init__( self) # initialize QObject in order to to use self.tr() repositories.load() plugins.getAllInstalled() if repositories.checkingOnStart() and repositories.timeForChecking( ) and repositories.allEnabled(): # start fetching repositories self.statusLabel = QLabel(iface.mainWindow().statusBar()) iface.mainWindow().statusBar().addPermanentWidget(self.statusLabel) self.statusLabel.linkActivated.connect( self.showPluginManagerWhenReady) repositories.checkingDone.connect(self.checkingDone) for key in repositories.allEnabled(): repositories.requestFetching(key) else: # no fetching at start, so mark all enabled repositories as requesting to be fetched. for key in repositories.allEnabled(): repositories.setRepositoryData(key, "state", 3) # look for obsolete plugins updates (the user-installed one is older than the core one) for key in plugins.obsoletePlugins: plugin = plugins.localCache[key] msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle(self.tr("QGIS Python Plugin Installer")) msg.addButton(self.tr("Uninstall (recommended)"), QMessageBox.AcceptRole) msg.addButton(self.tr("I will uninstall it later"), QMessageBox.RejectRole) msg.setText("%s <b>%s</b><br/><br/>%s" % ( self.tr("Obsolete plugin:"), plugin["name"], self. tr("QGIS has detected an obsolete plugin that masks its more recent version shipped with this copy of QGIS. This is likely due to files associated with a previous installation of QGIS. Do you want to remove the old plugin right now and unmask the more recent version?" ))) msg.exec_() if not msg.result(): # uninstall the update, update utils and reload if enabled self.uninstallPlugin(key, quiet=True) updateAvailablePlugins() settings = QgsSettings() if settings.value("/PythonPlugins/" + key, False, type=bool): settings.setValue("/PythonPlugins/watchDog/" + key, True) loadPlugin(key) startPlugin(key) settings.remove("/PythonPlugins/watchDog/" + key) # ----------------------------------------- # def fetchAvailablePlugins(self, reloadMode): """ Fetch plugins from all enabled repositories.""" """ reloadMode = true: Fully refresh data from QgsSettings to mRepositories """ """ reloadMode = false: Fetch unready repositories only """ QApplication.setOverrideCursor(Qt.WaitCursor) if reloadMode: repositories.load() plugins.clearRepoCache() plugins.getAllInstalled() for key in repositories.allEnabled(): if reloadMode or repositories.all( )[key]["state"] == 3: # if state = 3 (error or not fetched yet), try to fetch once again repositories.requestFetching(key) if repositories.fetchingInProgress(): fetchDlg = QgsPluginInstallerFetchingDialog(iface.mainWindow()) fetchDlg.exec_() del fetchDlg for key in repositories.all(): repositories.killConnection(key) QApplication.restoreOverrideCursor() # display error messages for every unavailable reposioty, unless Shift pressed nor all repositories are unavailable keepQuiet = QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers( Qt.ShiftModifier) if repositories.allUnavailable( ) and repositories.allUnavailable() != repositories.allEnabled(): for key in repositories.allUnavailable(): if not keepQuiet: QMessageBox.warning( iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self.tr("Error reading repository:") + " " + key + "\n\n" + repositories.all()[key]["error"]) if QgsApplication.keyboardModifiers() == Qt.KeyboardModifiers( Qt.ShiftModifier): keepQuiet = True # finally, rebuild plugins from the caches plugins.rebuild() # ----------------------------------------- # def checkingDone(self): """ Remove the "Looking for new plugins..." label and display a notification instead if any updates or news available """ if not self.statusLabel: # only proceed if the label is present return # rebuild plugins cache plugins.rebuild() # look for news in the repositories plugins.markNews() status = "" icon = "" # first check for news for key in plugins.all(): if plugins.all()[key]["status"] == "new": status = self.tr("There is a new plugin available") icon = "pluginNew.svg" tabIndex = 4 # PLUGMAN_TAB_NEW # then check for updates (and eventually overwrite status) for key in plugins.all(): if plugins.all()[key]["status"] == "upgradeable": status = self.tr("There is a plugin update available") icon = "pluginUpgrade.svg" tabIndex = 3 # PLUGMAN_TAB_UPGRADEABLE # finally set the notify label if status: self.statusLabel.setText( u'<a href="%d"><img src="qrc:/images/themes/default/%s"></a>' % (tabIndex, icon)) self.statusLabel.setToolTip(status) else: iface.mainWindow().statusBar().removeWidget(self.statusLabel) self.statusLabel = None # ----------------------------------------- # def exportRepositoriesToManager(self): """ Update manager's repository tree widget with current data """ iface.pluginManagerInterface().clearRepositoryList() for key in repositories.all(): url = repositories.all()[key]["url"] + repositories.urlParams() if repositories.inspectionFilter(): enabled = (key == repositories.inspectionFilter()) else: enabled = repositories.all()[key]["enabled"] iface.pluginManagerInterface().addToRepositoryList({ "name": key, "url": url, "enabled": enabled and "true" or "false", "valid": repositories.all()[key]["valid"] and "true" or "false", "state": str(repositories.all()[key]["state"]), "error": repositories.all()[key]["error"], "inspection_filter": repositories.inspectionFilter() and "true" or "false" }) # ----------------------------------------- # def exportPluginsToManager(self): """ Insert plugins metadata to QgsMetadataRegistry """ iface.pluginManagerInterface().clearPythonPluginMetadata() for key in plugins.all(): plugin = plugins.all()[key] iface.pluginManagerInterface().addPluginMetadata({ "id": key, "plugin_id": plugin["plugin_id"] or "", "name": plugin["name"], "description": plugin["description"], "about": plugin["about"], "category": plugin["category"], "tags": plugin["tags"], "changelog": plugin["changelog"], "author_name": plugin["author_name"], "author_email": plugin["author_email"], "homepage": plugin["homepage"], "tracker": plugin["tracker"], "code_repository": plugin["code_repository"], "version_installed": plugin["version_installed"], "library": plugin["library"], "icon": plugin["icon"], "readonly": plugin["readonly"] and "true" or "false", "installed": plugin["installed"] and "true" or "false", "available": plugin["available"] and "true" or "false", "status": plugin["status"], "error": plugin["error"], "error_details": plugin["error_details"], "experimental": plugin["experimental"] and "true" or "false", "deprecated": plugin["deprecated"] and "true" or "false", "trusted": plugin["trusted"] and "true" or "false", "version_available": plugin["version_available"], "zip_repository": plugin["zip_repository"], "download_url": plugin["download_url"], "filename": plugin["filename"], "downloads": plugin["downloads"], "average_vote": plugin["average_vote"], "rating_votes": plugin["rating_votes"], "pythonic": "true" }) iface.pluginManagerInterface().reloadModel() # ----------------------------------------- # def reloadAndExportData(self): """ Reload All repositories and export data to the Plugin Manager """ self.fetchAvailablePlugins(reloadMode=True) self.exportRepositoriesToManager() self.exportPluginsToManager() # ----------------------------------------- # def showPluginManagerWhenReady(self, *params): """ Open the plugin manager window. If fetching is still in progress, it shows the progress window first """ """ Optionally pass the index of tab to be opened in params """ if self.statusLabel: iface.mainWindow().statusBar().removeWidget(self.statusLabel) self.statusLabel = None self.fetchAvailablePlugins(reloadMode=False) self.exportRepositoriesToManager() self.exportPluginsToManager() # finally, show the plugin manager window tabIndex = -1 if len(params) == 1: indx = str(params[0]) if indx.isdigit() and int(indx) > -1 and int(indx) < 7: tabIndex = int(indx) iface.pluginManagerInterface().showPluginManager(tabIndex) # ----------------------------------------- # def onManagerClose(self): """ Call this method when closing manager window - it resets last-use-dependent values. """ plugins.updateSeenPluginsList() repositories.saveCheckingOnStartLastDate() # ----------------------------------------- # def exportSettingsGroup(self): """ Return QgsSettings settingsGroup value """ return settingsGroup # ----------------------------------------- # def upgradeAllUpgradeable(self): """ Reinstall all upgradeable plugins """ for key in plugins.allUpgradeable(): self.installPlugin(key, quiet=True) # ----------------------------------------- # def installPlugin(self, key, quiet=False): """ Install given plugin """ error = False infoString = ('', '') plugin = plugins.all()[key] previousStatus = plugin["status"] if not plugin: return if plugin["status"] == "newer" and not plugin[ "error"]: # ask for confirmation if user downgrades an usable plugin if QMessageBox.warning( iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), self. tr("Are you sure you want to downgrade the plugin to the latest available version? The installed one is newer!" ), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return dlg = QgsPluginInstallerInstallingDialog(iface.mainWindow(), plugin) dlg.exec_() if dlg.result(): error = True infoString = (self.tr("Plugin installation failed"), dlg.result()) elif not QDir(qgis.utils.home_plugin_path + "/" + key).exists(): error = True infoString = ( self.tr("Plugin has disappeared"), self. tr("The plugin seems to have been installed but I don't know where. Probably the plugin package contained a wrong named directory.\nPlease search the list of installed plugins. I'm nearly sure you'll find the plugin there, but I just can't determine which of them it is. It also means that I won't be able to determine if this plugin is installed and inform you about available updates. However the plugin may work. Please contact the plugin author and submit this issue." )) QApplication.setOverrideCursor(Qt.WaitCursor) plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() QApplication.restoreOverrideCursor() else: QApplication.setOverrideCursor(Qt.WaitCursor) # update the list of plugins in plugin handling routines updateAvailablePlugins() # try to load the plugin loadPlugin(plugin["id"]) plugins.getAllInstalled() plugins.rebuild() plugin = plugins.all()[key] if not plugin["error"]: if previousStatus in ["not installed", "new"]: infoString = (self.tr("Plugin installed successfully"), "") if startPlugin(plugin["id"]): settings = QgsSettings() settings.setValue("/PythonPlugins/" + plugin["id"], True) else: settings = QgsSettings() if settings.value( "/PythonPlugins/" + key, False, type=bool ): # plugin will be reloaded on the fly only if currently loaded reloadPlugin( key) # unloadPlugin + loadPlugin + startPlugin infoString = ( self.tr("Plugin reinstalled successfully"), "") else: unloadPlugin( key ) # Just for a case. Will exit quietly if really not loaded loadPlugin(key) infoString = ( self.tr("Plugin reinstalled successfully"), self. tr("Python plugin reinstalled.\nYou need to restart QGIS in order to reload it." )) if quiet: infoString = (None, None) QApplication.restoreOverrideCursor() else: QApplication.restoreOverrideCursor() if plugin["error"] == "incompatible": message = self.tr( "The plugin is not compatible with this version of QGIS. It's designed for QGIS versions:" ) message += " <b>" + plugin["error_details"] + "</b>" elif plugin["error"] == "dependent": message = self.tr( "The plugin depends on some components missing on your system. You need to install the following Python module in order to enable it:" ) message += "<b> " + plugin["error_details"] + "</b>" else: message = self.tr("The plugin is broken. Python said:") message += "<br><b>" + plugin["error_details"] + "</b>" dlg = QgsPluginInstallerPluginErrorDialog( iface.mainWindow(), message) dlg.exec_() if dlg.result(): # revert installation pluginDir = qgis.utils.home_plugin_path + "/" + plugin["id"] result = removeDir(pluginDir) if QDir(pluginDir).exists(): error = True infoString = (self.tr("Plugin uninstall failed"), result) try: exec("sys.path_importer_cache.clear()") exec("import %s" % plugin["id"]) exec("reload (%s)" % plugin["id"]) except: pass else: try: exec("del sys.modules[%s]" % plugin["id"]) except: pass plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() if infoString[0]: level = error and QgsMessageBar.CRITICAL or QgsMessageBar.INFO msg = "<b>%s</b>" % infoString[0] if infoString[1]: msg += "<b>:</b> %s" % infoString[1] iface.pluginManagerInterface().pushMessage(msg, level) # ----------------------------------------- # def uninstallPlugin(self, key, quiet=False): """ Uninstall given plugin """ if key in plugins.all(): plugin = plugins.all()[key] else: plugin = plugins.localCache[key] if not plugin: return if not quiet: warning = self.tr( "Are you sure you want to uninstall the following plugin?" ) + "\n(" + plugin["name"] + ")" if plugin["status"] == "orphan" and not plugin["error"]: warning += "\n\n" + self.tr( "Warning: this plugin isn't available in any accessible repository!" ) if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # unload the plugin QApplication.setOverrideCursor(Qt.WaitCursor) try: unloadPlugin(key) except: pass pluginDir = qgis.utils.home_plugin_path + "/" + plugin["id"] result = removeDir(pluginDir) if result: QApplication.restoreOverrideCursor() msg = "<b>%s:</b>%s" % (self.tr("Plugin uninstall failed"), result) iface.pluginManagerInterface().pushMessage(msg, QgsMessageBar.CRITICAL) else: # safe remove try: unloadPlugin(plugin["id"]) except: pass try: exec("plugins[%s].unload()" % plugin["id"]) exec("del plugins[%s]" % plugin["id"]) except: pass try: exec("del sys.modules[%s]" % plugin["id"]) except: pass plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() QApplication.restoreOverrideCursor() iface.pluginManagerInterface().pushMessage( self.tr("Plugin uninstalled successfully"), QgsMessageBar.INFO) # ----------------------------------------- # def addRepository(self): """ add new repository connection """ dlg = QgsPluginInstallerRepositoryDialog(iface.mainWindow()) dlg.editParams.setText(repositories.urlParams()) dlg.checkBoxEnabled.setCheckState(Qt.Checked) if not dlg.exec_(): return for i in list(repositories.all().values()): if dlg.editURL.text().strip() == i["url"]: iface.pluginManagerInterface().pushMessage( self.tr( "Unable to add another repository with the same URL!"), QgsMessageBar.WARNING) return settings = QgsSettings() settings.beginGroup(reposGroup) reposName = dlg.editName.text() reposURL = dlg.editURL.text().strip() if reposName in repositories.all(): reposName = reposName + "(2)" # add to settings settings.setValue(reposName + "/url", reposURL) settings.setValue(reposName + "/authcfg", dlg.editAuthCfg.text().strip()) settings.setValue(reposName + "/enabled", bool(dlg.checkBoxEnabled.checkState())) # refresh lists and populate widgets plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def editRepository(self, reposName): """ edit repository connection """ if not reposName: return checkState = {False: Qt.Unchecked, True: Qt.Checked} dlg = QgsPluginInstallerRepositoryDialog(iface.mainWindow()) dlg.editName.setText(reposName) dlg.editURL.setText(repositories.all()[reposName]["url"]) dlg.editAuthCfg.setText(repositories.all()[reposName]["authcfg"]) dlg.editParams.setText(repositories.urlParams()) dlg.checkBoxEnabled.setCheckState( checkState[repositories.all()[reposName]["enabled"]]) if repositories.all()[reposName]["valid"]: dlg.checkBoxEnabled.setEnabled(True) dlg.labelInfo.setText("") else: dlg.checkBoxEnabled.setEnabled(False) dlg.labelInfo.setText( self. tr("This repository is blocked due to incompatibility with your QGIS version" )) dlg.labelInfo.setFrameShape(QFrame.Box) if not dlg.exec_(): return # nothing to do if canceled for i in list(repositories.all().values()): if dlg.editURL.text().strip() == i["url"] and dlg.editURL.text( ).strip() != repositories.all()[reposName]["url"]: iface.pluginManagerInterface().pushMessage( self.tr( "Unable to add another repository with the same URL!"), QgsMessageBar.WARNING) return # delete old repo from QgsSettings and create new one settings = QgsSettings() settings.beginGroup(reposGroup) settings.remove(reposName) newName = dlg.editName.text() if newName in repositories.all() and newName != reposName: newName = newName + "(2)" settings.setValue(newName + "/url", dlg.editURL.text().strip()) settings.setValue(newName + "/authcfg", dlg.editAuthCfg.text().strip()) settings.setValue(newName + "/enabled", bool(dlg.checkBoxEnabled.checkState())) if dlg.editAuthCfg.text().strip() != repositories.all( )[reposName]["authcfg"]: repositories.all()[reposName]["authcfg"] = dlg.editAuthCfg.text( ).strip() if dlg.editURL.text().strip() == repositories.all( )[reposName]["url"] and dlg.checkBoxEnabled.checkState() == checkState[ repositories.all()[reposName]["enabled"]]: repositories.rename(reposName, newName) self.exportRepositoriesToManager() return # nothing else to do if only repository name was changed plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def deleteRepository(self, reposName): """ delete repository connection """ if not reposName: return settings = QgsSettings() settings.beginGroup(reposGroup) if settings.value(reposName + "/url", "", type=str) == officialRepo[1]: iface.pluginManagerInterface().pushMessage( self. tr("You can't remove the official QGIS Plugin Repository. You can disable it if needed." ), QgsMessageBar.WARNING) return warning = self.tr( "Are you sure you want to remove the following repository?" ) + "\n" + reposName if QMessageBox.warning(iface.mainWindow(), self.tr("QGIS Python Plugin Installer"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # delete from the settings, refresh data and repopulate all the widgets settings.remove(reposName) repositories.remove(reposName) plugins.removeRepository(reposName) self.reloadAndExportData() # ----------------------------------------- # def setRepositoryInspectionFilter(self, reposName=None): """ temporarily block another repositories to fetch only one for inspection """ repositories.setInspectionFilter(reposName) self.reloadAndExportData() # ----------------------------------------- # def sendVote(self, plugin_id, vote): """ send vote via the RPC """ if not plugin_id or not vote: return False url = "http://plugins.qgis.org/plugins/RPC2/" params = "{\"id\":\"djangorpc\",\"method\":\"plugin.vote\",\"params\":[%s,%s]}" % ( str(plugin_id), str(vote)) req = QNetworkRequest(QUrl(url)) req.setRawHeader("Content-Type", "application/json") QgsNetworkAccessManager.instance().post(req, params) return True def installFromZipFile(self, filePath): if not os.path.isfile(filePath): return settings = QgsSettings() settings.setValue(settingsGroup + '/lastZipDirectory', QFileInfo(filePath).absoluteDir().absolutePath()) error = False infoString = None with zipfile.ZipFile(filePath, 'r') as zf: pluginName = os.path.split(zf.namelist()[0])[0] pluginFileName = os.path.splitext(os.path.basename(filePath))[0] pluginsDirectory = qgis.utils.home_plugin_path if not QDir(pluginsDirectory).exists(): QDir().mkpath(pluginsDirectory) # If the target directory already exists as a link, # remove the link without resolving QFile(os.path.join(pluginsDirectory, pluginFileName)).remove() try: # Test extraction. If fails, then exception will be raised # and no removing occurs unzip(str(filePath), str(pluginsDirectory)) # Removing old plugin files if exist removeDir( QDir.cleanPath(os.path.join(pluginsDirectory, pluginFileName))) # Extract new files unzip(str(filePath), str(pluginsDirectory)) except: error = True infoString = ( self.tr("Plugin installation failed"), self. tr("Failed to unzip the plugin package\n{}.\nProbably it is broken" .format(filePath))) if infoString is None: updateAvailablePlugins() loadPlugin(pluginName) plugins.getAllInstalled() plugins.rebuild() self.exportPluginsToManager() if settings.contains('/PythonPlugins/' + pluginName): if settings.value('/PythonPlugins/' + pluginName, False, bool): startPlugin(pluginName) reloadPlugin(pluginName) else: unloadPlugin(pluginName) loadPlugin(pluginName) else: if startPlugin(pluginName): settings.setValue('/PythonPlugins/' + pluginName, True) infoString = (self.tr("Plugin installed successfully"), "") if infoString[0]: level = error and QgsMessageBar.CRITICAL or QgsMessageBar.INFO msg = "<b>%s</b>" % infoString[0] if infoString[1]: msg += "<b>:</b> %s" % infoString[1] iface.pluginManagerInterface().pushMessage(msg, level)
class TrackingDisplay(QToolBar): ''' Display the position of a mobile and add action for centering the map on the vehicle and erasing the track ''' def __init__(self, mobile, parent=None): super(TrackingDisplay, self).__init__(parent) self.setMovable(True) self.setFloatable(True) self.mobile = mobile self.timedOut = True self.lastFix = 0.0 s = QSettings() self.defFormat = s.value('PosiView/Misc/DefaultFormat', defaultValue=0, type=int) self.format = self.defFormat & 3 self.withSuff = cf.FlagDegreesUseStringSuffix if bool( self.defFormat & 4) else cf.FormatFlag(0) try: self.sep = cf.separator() + ' ' except AttributeError: self.sep = ', ' self.createActions() self.mobile.newPosition.connect(self.onNewPosition) self.mobile.timeout.connect(self.onTimeout) self.posText = '0.000000, 0.000000' def createActions(self): self.nameLabel = QLabel(self.mobile.name) self.nameLabel.setMinimumSize(80, 23) self.nameLabelAction = QWidgetAction(self) self.nameLabelAction.setDefaultWidget(self.nameLabel) self.addAction(self.nameLabelAction) self.enableAction = QAction("Enable Display", self) self.enableAction.setCheckable(True) self.enableAction.setChecked(True) icon = QIcon(':/plugins/PosiView/ledgrey.png') icon.addFile(':/plugins/PosiView/ledgreen.png', QSize(), QIcon.Normal, QIcon.On) self.enableAction.setIcon(icon) self.addAction(self.enableAction) self.enableAction.triggered.connect(self.onEnableClicked) self.enableAction.triggered.connect(self.mobile.setEnabled) self.addSeparator() self.posLabel = QLabel("--:--:-- 0.000000, 0.000000") self.posLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) widths = (180, 196, 204, 180, 184, 200, 208, 184) self.posLabel.setMinimumSize(widths[self.format], 23) self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') self.posLabelAction = QWidgetAction(self) self.posLabelAction.setDefaultWidget(self.posLabel) self.addAction(self.posLabelAction) self.centerAction = QAction(QIcon(':/plugins/PosiView/center.png'), "Center &Map", self) self.addAction(self.centerAction) self.deleteTrackAction = QAction( QIcon(':/plugins/PosiView/deletetrack.png'), 'Delete &Track', self) self.addAction(self.deleteTrackAction) self.deleteTrackAction.triggered.connect(self.mobile.deleteTrack) self.centerAction.triggered.connect(self.mobile.centerOnMap) @pyqtSlot(float, QgsPointXY, float, float) def onNewPosition(self, fix, pos, depth, altitude): s = str() if fix > 0: s = strftime('%H:%M:%S ', gmtime(fix)) else: s = '--:--:-- ' if self.format == 1: f, pr = cf.FormatDegreesMinutes, 4 elif self.format == 2: f, pr = cf.FormatDegreesMinutesSeconds, 2 else: f, pr = cf.FormatDecimalDegrees, 6 self.posText = self.sep.join( (cf.formatY(pos.y(), f, pr, self.withSuff), cf.formatX(pos.x(), f, pr, self.withSuff))) s += self.posText if depth > -9999: s += "\nd = {:.1f}".format(depth) if altitude > -9999: if depth > -9999: s += " alt = {:.1f}".format(altitude) else: s += "\nalt = {:.1f}".format(altitude) self.posLabel.setText(s) if self.timedOut: if fix > self.lastFix: self.posLabel.setStyleSheet( 'background: lime; font-size: 8pt; color: black;') self.timedOut = False self.lastFix = fix @pyqtSlot() def onTimeout(self): if not self.timedOut: self.timedOut = True self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') @pyqtSlot(bool) def onEnableClicked(self, enable): self.timedOut = True if enable: self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') else: self.posLabel.setStyleSheet( 'background: white; font-size: 8pt; color: black;') def mousePressEvent(self, event): if event.button() == Qt.LeftButton: if event.modifiers() == Qt.ControlModifier: QGuiApplication.clipboard().setText(self.posText) else: drag = QDrag(self) mimeData = QMimeData() mimeData.setText(self.posText) drag.setMimeData(mimeData) drag.exec_() def releaseMobile(self): self.mobile = None
class OptionsDialog(QDialog, FORM_CLASS): """Options dialog for the InaSAFE plugin.""" def __init__(self, iface, parent=None, qsetting=''): """Constructor for the dialog. :param iface: A Quantum GIS QgisAppInterface instance. :type iface: QgisAppInterface :param parent: Parent widget of this dialog :type parent: QWidget :param qsetting: String to specify the QSettings. By default, use empty string. :type qsetting: str """ QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'configure-inasafe.svg') self.setWindowIcon(QIcon(icon)) self.setWindowTitle(self.tr('InaSAFE %s Options' % get_version())) # Save reference to the QGIS interface and parent self.iface = iface self.parent = parent if qsetting: self.settings = QSettings(qsetting) else: self.settings = QSettings() # InaSAFE default values self.default_value_parameters = [] self.default_value_parameter_containers = [] # Flag for restore default values self.is_restore_default = False # List of setting key and control self.boolean_settings = { 'visibleLayersOnlyFlag': self.cbxVisibleLayersOnly, 'set_layer_from_title_flag': self.cbxSetLayerNameFromTitle, 'setZoomToImpactFlag': self.cbxZoomToImpact, 'set_show_only_impact_on_report': self.cbx_show_only_impact, 'print_atlas_report': self.cbx_print_atlas_report, 'setHideExposureFlag': self.cbxHideExposure, 'useSelectedFeaturesOnly': self.cbxUseSelectedFeaturesOnly, 'useSentry': self.cbxUseSentry, 'template_warning_verbose': self.template_warning_checkbox, 'showOrganisationLogoInDockFlag': self.organisation_on_dock_checkbox, 'developer_mode': self.cbxDevMode, 'generate_report': self.checkbox_generate_reports, 'memory_profile': self.check_box_memory, 'always_show_welcome_message': self.welcome_message_check_box } self.text_settings = { 'keywordCachePath': self.leKeywordCachePath, 'ISO19115_ORGANIZATION': self.organisation_line_edit, 'ISO19115_URL': self.website_line_edit, 'ISO19115_EMAIL': self.email_line_edit, 'ISO19115_LICENSE': self.license_line_edit, } # Export and Import button # Export button self.export_button = QPushButton(tr('Export')) # noinspection PyUnresolvedReferences self.export_button.clicked.connect(self.export_setting) self.button_box.addButton( self.export_button, QDialogButtonBox.ActionRole) # Import button self.import_button = QPushButton(tr('Import')) # noinspection PyUnresolvedReferences self.import_button.clicked.connect(self.import_setting) self.button_box.addButton( self.import_button, QDialogButtonBox.ActionRole) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Always set first tab to be open, 0-th index self.tabWidget.setCurrentIndex(0) # Hide not implemented group self.grpNotImplemented.hide() self.adjustSize() # Population parameter Tab # Label self.preference_label = QLabel() self.preference_label.setText(tr( 'Please set parameters for each hazard class below. Affected ' 'status and displacement rates selected on this tab are only ' 'applied to exposed populations. ' )) self.preference_layout.addWidget(self.preference_label) # Profile preference widget self.profile_widget = ProfileWidget() self.preference_layout.addWidget(self.profile_widget) # Demographic tab self.demographic_label = QLabel() self.demographic_label.setText(tr( 'Please set the global default demographic ratio below.')) self.default_values_layout.addWidget(self.demographic_label) self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) self.widget_container = QWidget() self.scroll_area.setWidget(self.widget_container) self.container_layout = QVBoxLayout() self.widget_container.setLayout(self.container_layout) self.default_values_layout.addWidget(self.scroll_area) # Restore state from setting self.restore_state() # Hide checkbox if not developers if not self.cbxDevMode.isChecked(): self.checkbox_generate_reports.hide() # Connections # Check boxes self.custom_north_arrow_checkbox.toggled.connect(self.set_north_arrow) self.custom_UseUserDirectory_checkbox.toggled.connect( self.set_user_dir) self.custom_templates_dir_checkbox.toggled.connect( self.set_templates_dir) self.custom_org_disclaimer_checkbox.toggled.connect( self.set_org_disclaimer) self.custom_organisation_logo_check_box.toggled.connect( self.toggle_logo_path) # Buttons self.toolKeywordCachePath.clicked.connect(self.open_keyword_cache_path) self.toolUserDirectoryPath.clicked.connect( self.open_user_directory_path) self.toolNorthArrowPath.clicked.connect(self.open_north_arrow_path) self.open_organisation_logo_path_button.clicked.connect( self.open_organisation_logo_path) self.toolReportTemplatePath.clicked.connect( self.open_report_template_path) # Others self.organisation_logo_path_line_edit.textChanged.connect( self.update_logo_preview) self.earthquake_function.currentIndexChanged.connect( self.update_earthquake_info) # Set up listener for restore defaults button self.demographic_restore_defaults = self.button_box_restore_defaults.\ button(QDialogButtonBox.RestoreDefaults) self.demographic_restore_defaults.setText( self.demographic_restore_defaults.text().capitalize()) self.demographic_restore_defaults.setCheckable(True) self.demographic_restore_defaults.clicked.connect( self.restore_defaults_ratio) # Restore button in population parameter tab self.parameter_population_restore_button = \ self.button_box_restore_preference.button( QDialogButtonBox.RestoreDefaults) self.parameter_population_restore_button.setText( self.parameter_population_restore_button.text().capitalize()) self.parameter_population_restore_button.clicked.connect( partial(self.restore_population_parameters, global_default=True)) # TODO: Hide this until behaviour is defined # hide template warning toggle self.template_warning_checkbox.hide() # hide custom template dir toggle self.custom_templates_dir_checkbox.hide() self.splitter_custom_report.hide() # Welcome message self.set_welcome_message() def save_boolean_setting(self, key, check_box): """Save boolean setting according to check_box state. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ set_setting(key, check_box.isChecked(), qsettings=self.settings) def restore_boolean_setting(self, key, check_box): """Set check_box according to setting of key. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ flag = setting(key, expected_type=bool, qsettings=self.settings) check_box.setChecked(flag) def save_text_setting(self, key, line_edit): """Save text setting according to line_edit value. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ set_setting(key, line_edit.text(), self.settings) def restore_text_setting(self, key, line_edit): """Set line_edit text according to setting of key. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ value = setting(key, expected_type=str, qsettings=self.settings) line_edit.setText(value) def restore_state(self): """Reinstate the options based on the user's stored session info.""" # Restore boolean setting as check box. for key, check_box in list(self.boolean_settings.items()): self.restore_boolean_setting(key, check_box) # Restore text setting as line edit. for key, line_edit in list(self.text_settings.items()): self.restore_text_setting(key, line_edit) # User Directory user_directory_path = setting( key='defaultUserDirectory', default=temp_dir('impacts'), expected_type=str, qsettings=self.settings) custom_user_directory_flag = ( user_directory_path != temp_dir('impacts')) self.custom_UseUserDirectory_checkbox.setChecked( custom_user_directory_flag) self.splitter_user_directory.setEnabled(custom_user_directory_flag) self.leUserDirectoryPath.setText(user_directory_path) # Currency # Populate the currency list for currency in currencies: self.currency_combo_box.addItem(currency['name'], currency['key']) # Then make selected the default one. default_currency = setting('currency', expected_type=str) keys = [currency['key'] for currency in currencies] if default_currency not in keys: default_currency = currencies[0]['key'] index = self.currency_combo_box.findData(default_currency) self.currency_combo_box.setCurrentIndex(index) # Earthquake function. # Populate the combobox first. for model in EARTHQUAKE_FUNCTIONS: self.earthquake_function.addItem(model['name'], model['key']) # Then make selected the default one. default_earthquake_function = setting( 'earthquake_function', expected_type=str) keys = [model['key'] for model in EARTHQUAKE_FUNCTIONS] if default_earthquake_function not in keys: default_earthquake_function = EARTHQUAKE_FUNCTIONS[0]['key'] index = self.earthquake_function.findData(default_earthquake_function) self.earthquake_function.setCurrentIndex(index) self.update_earthquake_info() # Restore North Arrow Image Path north_arrow_path = setting( key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) custom_north_arrow_flag = ( north_arrow_path != default_north_arrow_path()) self.custom_north_arrow_checkbox.setChecked(custom_north_arrow_flag) self.splitter_north_arrow.setEnabled(custom_north_arrow_flag) self.leNorthArrowPath.setText(north_arrow_path) # Restore Report Template Directory Path report_template_directory = setting( key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) custom_templates_dir_flag = (report_template_directory != '') self.custom_templates_dir_checkbox.setChecked( custom_templates_dir_flag) self.leReportTemplatePath.setText(report_template_directory) # Restore Disclaimer org_disclaimer = setting( key='reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) custom_org_disclaimer_flag = (org_disclaimer != disclaimer()) self.custom_org_disclaimer_checkbox.setChecked( custom_org_disclaimer_flag) self.txtDisclaimer.setPlainText(org_disclaimer) # Restore Organisation Logo Path org_logo_path = setting( key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) # Check if the path is default one or not custom_org_logo_flag = org_logo_path != supporters_logo_path() self.organisation_logo_path_line_edit.setText(org_logo_path) self.custom_organisation_logo_check_box.setChecked( custom_org_logo_flag) self.organisation_logo_path_line_edit.setEnabled( custom_org_logo_flag) self.open_organisation_logo_path_button.setEnabled( custom_org_logo_flag) # Manually call here self.update_logo_preview() # Restore InaSAFE default values self.restore_default_values_page() # Restore Population Parameter self.restore_population_parameters(global_default=False) def save_state(self): """Store the options into the user's stored session info.""" # Save boolean settings for key, check_box in list(self.boolean_settings.items()): self.save_boolean_setting(key, check_box) # Save text settings for key, line_edit in list(self.text_settings.items()): self.save_text_setting(key, line_edit) set_setting( 'north_arrow_path', self.leNorthArrowPath.text(), self.settings) set_setting( 'organisation_logo_path', self.organisation_logo_path_line_edit.text(), self.settings) set_setting( 'reportTemplatePath', self.leReportTemplatePath.text(), self.settings) set_setting( 'reportDisclaimer', self.txtDisclaimer.toPlainText(), self.settings) set_setting( 'defaultUserDirectory', self.leUserDirectoryPath.text(), self.settings) index = self.earthquake_function.currentIndex() value = self.earthquake_function.itemData(index) set_setting('earthquake_function', value, qsettings=self.settings) currency_index = self.currency_combo_box.currentIndex() currency_key = self.currency_combo_box.itemData(currency_index) set_setting('currency', currency_key, qsettings=self.settings) # Save InaSAFE default values self.save_default_values() # Save population parameters self.save_population_parameters() def accept(self): """Method invoked when OK button is clicked.""" self.save_state() super(OptionsDialog, self).accept() def update_earthquake_info(self): """Update information about earthquake info.""" self.label_earthquake_model() current_index = self.earthquake_function.currentIndex() model = EARTHQUAKE_FUNCTIONS[current_index] notes = '' for note in model['notes']: notes += note + '\n\n' citations = '' for citation in model['citations']: citations += citation['text'] + '\n\n' text = tr( 'Description:\n\n%s\n\n' 'Notes:\n\n%s\n\n' 'Citations:\n\n%s') % ( model['description'], notes, citations) self.earthquake_fatality_model_notes.setText(text) def label_earthquake_model(self): model = self.earthquake_function.currentText() help_text = tr( 'Please select your preferred earthquake fatality model. The ' 'default fatality model is the {model}.').format(model=model) self.label_default_earthquake.setText(help_text) def open_keyword_cache_path(self): """Open File dialog to choose the keyword cache path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getSaveFileName( self, self.tr('Set keyword cache file'), self.leKeywordCachePath.text(), self.tr('Sqlite DB File (*.db)')) if file_name: self.leKeywordCachePath.setText(file_name) def open_user_directory_path(self): """Open File dialog to choose the user directory path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Results directory'), self.leUserDirectoryPath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leUserDirectoryPath.setText(directory_name) def open_north_arrow_path(self): """Open File dialog to choose the north arrow path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set north arrow image file'), self.leNorthArrowPath.text(), self.tr( 'Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.leNorthArrowPath.setText(file_name) def open_organisation_logo_path(self): """Open File dialog to choose the organisation logo path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set organisation logo file'), self.organisation_logo_path_line_edit.text(), self.tr( 'Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.organisation_logo_path_line_edit.setText(file_name) def open_report_template_path(self): """Open File dialog to choose the report template path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Templates directory'), self.leReportTemplatePath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leReportTemplatePath.setText(directory_name) def toggle_logo_path(self): """Set state of logo path line edit and button.""" is_checked = self.custom_organisation_logo_check_box.isChecked() if is_checked: # Use previous org logo path path = setting( key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) else: # Set organisation path line edit to default one path = supporters_logo_path() self.organisation_logo_path_line_edit.setText(path) self.organisation_logo_path_line_edit.setEnabled(is_checked) self.open_organisation_logo_path_button.setEnabled(is_checked) def update_logo_preview(self): """Update logo based on the current logo path.""" logo_path = self.organisation_logo_path_line_edit.text() if os.path.exists(logo_path): icon = QPixmap(logo_path) label_size = self.organisation_logo_label.size() label_size.setHeight(label_size.height() - 2) label_size.setWidth(label_size.width() - 2) scaled_icon = icon.scaled( label_size, Qt.KeepAspectRatio) self.organisation_logo_label.setPixmap(scaled_icon) else: self.organisation_logo_label.setText(tr("Logo not found")) def set_north_arrow(self): """Auto-connect slot activated when north arrow checkbox is toggled.""" is_checked = self.custom_north_arrow_checkbox.isChecked() if is_checked: # Show previous north arrow path path = setting( key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) else: # Set the north arrow line edit to default one path = default_north_arrow_path() self.leNorthArrowPath.setText(path) self.splitter_north_arrow.setEnabled(is_checked) def set_user_dir(self): """Auto-connect slot activated when user dir checkbox is toggled. """ is_checked = self.custom_UseUserDirectory_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting( key='defaultUserDirectory', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = temp_dir('impacts') self.leUserDirectoryPath.setText(path) self.splitter_user_directory.setEnabled(is_checked) def set_templates_dir(self): """Auto-connect slot activated when templates dir checkbox is toggled. """ is_checked = self.custom_templates_dir_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting( key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = '' self.leReportTemplatePath.setText(path) self.splitter_custom_report.setEnabled(is_checked) def set_org_disclaimer(self): """Auto-connect slot activated when org disclaimer checkbox is toggled. """ is_checked = self.custom_org_disclaimer_checkbox.isChecked() if is_checked: # Show previous organisation disclaimer org_disclaimer = setting( 'reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) else: # Set the organisation disclaimer to the default one org_disclaimer = disclaimer() self.txtDisclaimer.setPlainText(org_disclaimer) self.txtDisclaimer.setEnabled(is_checked) @pyqtSlot(bool) # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = options_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def restore_default_values_page(self): """Setup UI for default values setting.""" # Clear parameters so it doesn't add parameters when # restore from changes. if self.default_value_parameters: self.default_value_parameters = [] if self.default_value_parameter_containers: self.default_value_parameter_containers = [] for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) default_fields = all_default_fields() for field_group in all_field_groups: settable_fields = [] for field in field_group['fields']: if field not in default_fields: continue else: settable_fields.append(field) default_fields.remove(field) if not settable_fields: continue # Create group box for each field group group_box = QGroupBox(self) group_box.setTitle(field_group['name']) self.container_layout.addWidget(group_box) parameters = [] for settable_field in settable_fields: parameter = self.default_field_to_parameter(settable_field) if parameter: parameters.append(parameter) parameter_container = ParameterContainer( parameters, description_text=field_group['description'], extra_parameters=extra_parameter ) parameter_container.setup_ui(must_scroll=False) group_box_inner_layout = QVBoxLayout() group_box_inner_layout.addWidget(parameter_container) group_box.setLayout(group_box_inner_layout) # Add to attribute self.default_value_parameter_containers.append(parameter_container) # Only show non-groups default fields if there is one if len(default_fields) > 0: for default_field in default_fields: parameter = self.default_field_to_parameter(default_field) if parameter: self.default_value_parameters.append(parameter) description_text = tr( 'In this options you can change the global default values for ' 'these variables.') parameter_container = ParameterContainer( self.default_value_parameters, description_text=description_text, extra_parameters=extra_parameter ) parameter_container.setup_ui(must_scroll=False) self.other_group_box = QGroupBox(tr('Non-group fields')) other_group_inner_layout = QVBoxLayout() other_group_inner_layout.addWidget(parameter_container) self.other_group_box.setLayout(other_group_inner_layout) self.container_layout.addWidget(self.other_group_box) # Add to attribute self.default_value_parameter_containers.append(parameter_container) def restore_population_parameters(self, global_default=True): """Setup UI for population parameter page from setting. :param global_default: If True, set to original default (from the value in definitions). :type global_default: bool """ if global_default: data = generate_default_profile() else: data = setting('population_preference', generate_default_profile()) if not isinstance(data, dict): LOGGER.debug( 'population parameter is not a dictionary. InaSAFE will use ' 'the default one.') data = generate_default_profile() try: self.profile_widget.data = data except KeyError as e: LOGGER.debug( 'Population parameter is not in correct format. InaSAFE will ' 'use the default one.') LOGGER.debug(e) data = generate_default_profile() self.profile_widget.data = data @staticmethod def age_ratios(): """Helper to get list of age ratio from the options dialog. :returns: List of age ratio. :rtype: list """ # FIXME(IS) set a correct parameter container parameter_container = None youth_ratio = parameter_container.get_parameter_by_guid( youth_ratio_field['key']).value adult_ratio = parameter_container.get_parameter_by_guid( adult_ratio_field['key']).value elderly_ratio = parameter_container.get_parameter_by_guid( elderly_ratio_field['key']).value ratios = [youth_ratio, adult_ratio, elderly_ratio] return ratios def is_good_age_ratios(self): """Method to check the sum of age ratio is 1. :returns: True if the sum is 1 or the sum less than 1 but there is None. :rtype: bool """ ratios = self.age_ratios() if None in ratios: # If there is None, just check to not exceeding 1 clean_ratios = [x for x in ratios if x is not None] ratios.remove(None) if sum(clean_ratios) > 1: return False else: if sum(ratios) != 1: return False return True def save_default_values(self): """Save InaSAFE default values.""" for parameter_container in self.default_value_parameter_containers: parameters = parameter_container.get_parameters() for parameter in parameters: set_inasafe_default_value_qsetting( self.settings, GLOBAL, parameter.guid, parameter.value ) def restore_defaults_ratio(self): """Restore InaSAFE default ratio.""" # Set the flag to true because user ask to. self.is_restore_default = True # remove current default ratio for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) # reload default ratio self.restore_default_values_page() def default_field_to_parameter(self, default_field): """Obtain parameter from default field. :param default_field: A default field definition. :type default_field: dict :returns: A parameter object. :rtype: FloatParameter, IntegerParameter """ if default_field.get('type') == QVariant.Double: parameter = FloatParameter() elif default_field.get('type') in qvariant_whole_numbers: parameter = IntegerParameter() else: return default_value = default_field.get('default_value') if not default_value: message = ( 'InaSAFE default field %s does not have default value' % default_field.get('name')) LOGGER.exception(message) return parameter.guid = default_field.get('key') parameter.name = default_value.get('name') parameter.is_required = True parameter.precision = default_field.get('precision') parameter.minimum_allowed_value = default_value.get( 'min_value', 0) parameter.maximum_allowed_value = default_value.get( 'max_value', 100000000) parameter.help_text = default_value.get('help_text') parameter.description = default_value.get('description') # Check if user ask to restore to the most default value. if self.is_restore_default: parameter._value = default_value.get('default_value') else: # Current value qsetting_default_value = get_inasafe_default_value_qsetting( self.settings, GLOBAL, default_field['key']) # To avoid python error if qsetting_default_value > parameter.maximum_allowed_value: qsetting_default_value = parameter.maximum_allowed_value if qsetting_default_value < parameter.minimum_allowed_value: qsetting_default_value = parameter.minimum_allowed_value parameter.value = qsetting_default_value return parameter def save_population_parameters(self): """Helper to save population parameter to QSettings.""" population_parameter = self.profile_widget.data set_setting('population_preference', population_parameter) def set_welcome_message(self): """Create and insert welcome message.""" string = html_header() string += welcome_message().to_html() string += html_footer() self.welcome_message.setHtml(string) def show_option_dialog(self): """Helper to show usual option dialog (without welcome message tab).""" self.tabWidget.removeTab(0) def show_welcome_dialog(self): """Setup for showing welcome message dialog. This method will setup several things: - Only show welcome, organisation profile, and population parameter tab. Currently, they are the first 3 tabs. - Set the title - Move the check box for always showing welcome message. """ self.welcome_layout.addWidget(self.welcome_message_check_box) while self.tabWidget.count() > 3: self.tabWidget.removeTab(self.tabWidget.count() - 1) self.setWindowTitle(self.tr('Welcome to InaSAFE %s' % get_version())) # Hide the export import button self.export_button.hide() self.import_button.hide() def export_setting(self): """Export setting from an existing file.""" LOGGER.debug('Export button clicked') home_directory = os.path.expanduser('~') file_name = self.organisation_line_edit.text().replace(' ', '_') file_path, __ = QFileDialog.getSaveFileName( self, self.tr('Export InaSAFE settings'), os.path.join(home_directory, file_name + '.json'), self.tr('JSON File (*.json)')) if file_path: LOGGER.debug('Exporting to %s' % file_path) export_setting(file_path) def import_setting(self): """Import setting to a file.""" LOGGER.debug('Import button clicked') home_directory = os.path.expanduser('~') file_path, __ = QFileDialog.getOpenFileName( self, self.tr('Import InaSAFE settings'), home_directory, self.tr('JSON File (*.json)')) if file_path: title = tr('Import InaSAFE Settings.') question = tr( 'This action will replace your current InaSAFE settings with ' 'the setting from the file. This action is not reversible. ' 'Are you sure to import InaSAFE Setting?') answer = QMessageBox.question( self, title, question, QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.Yes: LOGGER.debug('Import from %s' % file_path) import_setting(file_path)
def __init__(self, parent, alg): ParametersPanel.__init__(self, parent, alg) ## Création le l'interface Qt pour les paramètres ## Add console command w = QWidget() # New Qt Windows layout = QVBoxLayout() # New Qt vertical Layout layout.setMargin(0) layout.setSpacing(6) label = QLabel() # New Qt label (text) label.setText(self.tr("Chloe/Java console call")) layout.addWidget(label) # Add label in layout self.text = QPlainTextEdit() # New Qt champs de text in/out self.text.setReadOnly(True) # Read only layout.addWidget(self.text) # Add in layout w.setLayout(layout) # layout -in-> Windows self.layoutMain.addWidget(w) # windows -in-> Windows system self.types_of_metrics = {} for output in self.alg.outputs: if isinstance(output, (OutputRaster, OutputVector, OutputTable)): self.checkBoxes[output.name].setText(self.tr('Open output file after running algorithm')) self.connectParameterSignals() self.parametersHaveChanged() self.changeWindowShapeDependent() # === param:WINDOW_SIZES Widget:IntListSelectionPanel === lineEdit = self.widgets["WINDOW_SIZES"].lineEdit lineEdit.setReadOnly(True) sbInt = self.widgets["WINDOW_SIZES"].sbInt sbInt.setValue(3) # === param:METRICS Widget:ListSelectionPanel === # === Init cbFilter cbFilter = self.widgets["METRICS"].cbFilter cbFilter.addItems(self.alg.types_of_metrics.keys()) # === Init listSrc value = cbFilter.currentText() listSrc = self.widgets["METRICS"].listSrc listSrc.clear() if self.types_of_metrics: if value in self.types_of_metrics.keys(): listSrc.addItems(self.types_of_metrics[value]) # === Init listDest listDest = self.widgets["METRICS"].listDest listDest.clear() self.metrics_selected = set() self.window_sizes_selected = set() lineEdit = self.widgets["METRICS"].lineEdit lineEdit.setReadOnly(True)
def _setupUI(self): self.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # deselected widget self.deselected_widget = QListWidget(self) self._set_list_widget_defaults(self.deselected_widget) deselected_label = QLabel() deselected_label.setText('Deselected') deselected_label.setAlignment(Qt.AlignCenter) deselected_label.setFont(italic_font) deselected_v_layout = QVBoxLayout() deselected_v_layout.addWidget(deselected_label) deselected_v_layout.addWidget(self.deselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText('Selected') selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton('>>') self.deselect_all_btn = SmallQPushButton('<<') self.select_btn = SmallQPushButton('>') self.deselect_btn = SmallQPushButton('<') self.select_btn.setToolTip('Add the selected items') self.deselect_btn.setToolTip('Remove the selected items') self.select_all_btn.setToolTip('Add all') self.deselect_all_btn.setToolTip('Remove all') # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(deselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout)
class TrackingDisplay(QToolBar): ''' Display the position of a mobile and add action for centering the map on the vehicle and erasing the track ''' def __init__(self, mobile, parent=None): super(TrackingDisplay, self).__init__(parent) self.setMovable(True) self.setFloatable(True) self.mobile = mobile self.timedOut = True self.lastFix = 0.0 s = QSettings() self.defFormat = s.value('PosiView/Misc/DefaultFormat', defaultValue=0, type=int) self.format = self.defFormat & 3 self.withSuff = QgsCoordinateFormatter.FlagDegreesUseStringSuffix if bool(self.defFormat & 4) else QgsCoordinateFormatter.FormatFlag(0) self.createActions() self.mobile.newPosition.connect(self.onNewPosition) self.mobile.timeout.connect(self.onTimeout) def createActions(self): self.nameLabel = QLabel(self.mobile.name) self.nameLabel.setMinimumSize(80, 23) self.nameLabelAction = QWidgetAction(self) self.nameLabelAction.setDefaultWidget(self.nameLabel) self.addAction(self.nameLabelAction) self.enableAction = QAction("Enable Display", self) self.enableAction.setCheckable(True) self.enableAction.setChecked(True) icon = QIcon(':/plugins/PosiView/ledgrey.png') icon.addFile(':/plugins/PosiView/ledgreen.png', QSize(), QIcon.Normal, QIcon.On) self.enableAction.setIcon(icon) self.addAction(self.enableAction) self.enableAction.triggered.connect(self.onEnableClicked) self.enableAction.triggered.connect(self.mobile.setEnabled) self.addSeparator() self.posLabel = QLabel("--:--:-- 0.000000 0.000000") self.posLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) widths = (180, 196, 204, 180, 184, 200, 208, 184) self.posLabel.setMinimumSize(widths[self.format], 23) self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') self.posLabelAction = QWidgetAction(self) self.posLabelAction.setDefaultWidget(self.posLabel) self.addAction(self.posLabelAction) self.centerAction = QAction(QIcon(':/plugins/PosiView/center.png'), "Center &Map", self) self.addAction(self.centerAction) self.deleteTrackAction = QAction(QIcon(':/plugins/PosiView/deletetrack.png'), 'Delete &Track', self) self.addAction(self.deleteTrackAction) self.deleteTrackAction.triggered.connect(self.mobile.deleteTrack) self.centerAction.triggered.connect(self.mobile.centerOnMap) @pyqtSlot(float, QgsPointXY, float, float) def onNewPosition(self, fix, pos, depth, altitude): s = str() if fix > 0: s = strftime('%H:%M:%S ', gmtime(fix)) else: s = '--:--:-- ' if self.format == 0: s += "{:f} {:f}".format(pos.y(), pos.x()) elif self.format == 1: s += ', '.join(QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutes, 4, self.withSuff ).rsplit(',')[::-1]) else: s += ', '.join(QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutesSeconds, 2, self.withSuff ).rsplit(',')[::-1]) if depth > -9999: s += "\nd = {:.1f}".format(depth) if altitude > -9999: if depth > -9999: s += " alt = {:.1f}".format(altitude) else: s += "\nalt = {:.1f}".format(altitude) self.posLabel.setText(s) if self.timedOut: if fix > self.lastFix: self.posLabel.setStyleSheet('background: lime; font-size: 8pt; color: black;') self.timedOut = False self.lastFix = fix @pyqtSlot() def onTimeout(self): if not self.timedOut: self.timedOut = True self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') @pyqtSlot(bool) def onEnableClicked(self, enable): self.timedOut = True if enable: self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') else: self.posLabel.setStyleSheet('background: white; font-size: 8pt; color: black;') def releaseMobile(self): self.mobile = None
class MainWindow(QMainWindow): def __init__(self, iface): QMainWindow.__init__(self) self.resize(600, 600) self.layer = None self.iface = iface self.dstpoints = [] self.srcpoints = [] self.filename = None self.cursrc = None self.curdst = None self.xform_m2r = None self.xform_r2m = None self.canvas = QgsMapCanvas(self) #self.canvas.setCachingEnabled(True) self.canvas.setCanvasColor(Qt.white) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.setCentralWidget(self.canvas) def t(name): return QgsApplication.getThemeIcon('/' + name) actionZoomIn = QAction(t('mActionZoomIn.svg'), "Zoom in", self) actionZoomOut = QAction(t('mActionZoomOut.svg'), "Zoom out", self) actionZoomToLayer = QAction(t('mActionZoomToLayer.svg'), "Zoom To Layer", self) actionZoomToDest = QAction("Zoom To Map", self) actionPan = QAction(t('mActionPan.svg'), "Pan", self) actionOpen = QAction(t('mActionFileOpen.svg'), "Open", self) actionSave = QAction(t('mActionFileSaveAs.svg'), "Save", self) actionAdd = QAction("Add GCP", self) actionDeleteAll = QAction("Delete All GCPs", self) actionZoomIn.setCheckable(True) actionZoomOut.setCheckable(True) actionPan.setCheckable(True) actionAdd.setCheckable(True) actionZoomIn.triggered.connect(self.zoomIn) actionZoomOut.triggered.connect(self.zoomOut) actionZoomToLayer.triggered.connect(self.zoomToLayer) actionZoomToDest.triggered.connect(self.zoomToDest) actionPan.triggered.connect(self.pan) actionOpen.triggered.connect(self.showOpen) actionSave.triggered.connect(self.savePoints) actionAdd.triggered.connect(self.addGcp) actionDeleteAll.triggered.connect(self.deleteAll) self.toolbar = self.addToolBar("Canvas actions") self.toolbar.addAction(actionOpen) self.toolbar.addAction(actionSave) self.toolbar.addSeparator() self.toolbar.addAction(actionPan) self.toolbar.addAction(actionZoomIn) self.toolbar.addAction(actionZoomOut) self.toolbar.addAction(actionZoomToLayer) self.toolbar.addAction(actionZoomToDest) self.toolbar.addSeparator() self.toolbar.addAction(actionAdd) self.toolbar.addSeparator() self.toolbar.addAction(actionDeleteAll) self.toolPan = QgsMapToolPan(self.canvas) self.toolPan.setAction(actionPan) self.toolZoomIn = QgsMapToolZoom(self.canvas, False) self.toolZoomIn.setAction(actionZoomIn) self.toolZoomOut = QgsMapToolZoom(self.canvas, True) self.toolZoomOut.setAction(actionZoomOut) self.toolSrcAdd = CaptureTool(self.canvas, self.iface.cadDockWidget()) self.toolSrcAdd.pointSelected.connect(self.addSrcPoint) self.toolSrcAdd.pointDeleted.connect(self.delSrcPoint) self.toolDestAdd = CaptureTool(self.iface.mapCanvas(), self.iface.cadDockWidget()) self.toolDestAdd.pointSelected.connect(self.addDestPoint) self.toolDestAdd.pointDeleted.connect(self.delDestPoint) #self.toolDestAdd.setAction(actionAdd) self.pan() self.overview = QgsMapOverviewCanvas(None, self.canvas) self.overview_dock = QDockWidget("Overview") self.overview_dock.setWidget(self.overview) self.addDockWidget(Qt.BottomDockWidgetArea, self.overview_dock) self.canvas.enableOverviewMode(self.overview) statusbar = self.statusBar() self.coordslabel = QLabel(statusbar) self.canvas.xyCoordinates.connect(self.showMouseCoordinates) statusbar.addPermanentWidget(self.coordslabel) def __del__(self): for x in self.dstpoints: self.iface.mapCanvas().scene().removeItem(x) def zoomIn(self): self.canvas.setMapTool(self.toolZoomIn) def zoomOut(self): self.canvas.setMapTool(self.toolZoomOut) def addGcp(self): self.canvas.setMapTool(self.toolSrcAdd) self.iface.mapCanvas().setMapTool(self.toolDestAdd) def addDestPoint(self, pt): if self.curdst: self.curdst.setPoint(pt) else: self.curdst = CanvasItem(self.iface.mapCanvas(), pt) self.curdst.setCompleted(False) self.registerPoint() def addSrcPoint(self, pt): if self.cursrc: self.cursrc.setPoint(pt) else: self.cursrc = CanvasItem(self.canvas, pt) self.cursrc.setCompleted(False) self.registerPoint() def delDestPoint(self, pt): idx = self.dstpoints.index(pt) self.iface.mapCanvas().scene().removeItem(self.dstpoints[idx]) del self.dstpoints[idx] self.canvas.scene().removeItem(self.srcpoints[idx]) del self.srcpoints[idx] def delSrcPoint(self, pt): idx = self.srcpoints.index(pt) self.iface.mapCanvas().scene().removeItem(self.dstpoints[idx]) del self.dstpoints[idx] self.canvas.scene().removeItem(self.srcpoints[idx]) del self.srcpoints[idx] def registerPoint(self): if not self.cursrc or not self.curdst: return self.cursrc.setCompleted(True) self.cursrc.setDirty(True) self.curdst.setCompleted(True) self.curdst.setDirty(True) self.srcpoints.append(self.cursrc) self.dstpoints.append(self.curdst) self.cursrc = None self.curdst = None def deleteAll(self): for x in self.dstpoints: self.iface.mapCanvas().scene().removeItem(x) for x in self.srcpoints: self.canvas.scene().removeItem(x) self.dstpoints = [] self.srcpoints = [] def zoomToLayer(self): if self.layer: self.canvas.setExtent(self.layer.extent()) self.canvas.refresh() def zoomToDest(self): pts = [x.pt for x in self.dstpoints] if len(pts) < 2: return r = QgsRectangle(pts[0], pts[1]) for pt in pts[2:]: r.include(pt) self.iface.mapCanvas().setExtent(r) self.iface.mapCanvas().refresh() def pan(self): self.canvas.setMapTool(self.toolPan) def showOpen(self): startdir = '.' filters = QgsProviderRegistry.instance().fileRasterFilters() lastFilter = "Multi-resolution Seamless Image Database (*.sid *.SID)" dlg = QFileDialog(self) dlg.setWindowTitle("Open raster") dlg.setFileMode(QFileDialog.ExistingFile) dlg.setNameFilter(filters) dlg.selectNameFilter(lastFilter) dlg.fileSelected.connect(self.openFile) dlg.show() def openFile(self, filename): if not filename: return if self.layer: QgsMapLayerRegistry.instance().removeMapLayers([self.layer.id()]) self.deleteAll() self.setWindowTitle(os.path.basename(filename)) self.filename = filename with CrsDialogSuspender(): self.layer = QgsRasterLayer(filename, "Raster") QgsMapLayerRegistry.instance().addMapLayers([self.layer], False) self.canvas.setLayerSet([QgsMapCanvasLayer(self.layer, True, True)]) self.canvas.setExtent(self.layer.extent()) # figure out source file transform ds = gdal.Open(filename) xform = ds.GetGeoTransform(True) if not xform: # qgis inverts y when the transform does not exist xform = [0.0, 1.0, 0.0, 0.0, 0.0, -1.0] self.xform_r2m = xform invxform = gdal.InvGeoTransform(xform) if len(invxform) == 2: invxform = invxform[1] self.xform_m2r = invxform # load points gcp_filename = filename + ".gcps" if os.path.exists(gcp_filename): self.loadPoints(gcp_filename) self.zoomToLayer() def map2raster(self, x, y): return gdal.ApplyGeoTransform(self.xform_m2r, x, y) def raster2map(self, x, y): return gdal.ApplyGeoTransform(self.xform_r2m, x, y) def loadPoints(self, filename): self.srcpoints = [] self.dstpoints = [] for line in open(filename, 'rt').readlines(): line.strip() if line.startswith('map'): continue parts = line.split(',') if len(parts) < 4: continue dstpt = QgsPoint(float(parts[0]), float(parts[1])) oldsrcx, oldsrcy = float(parts[2]), float(parts[3]) newsrcx, newsrcy = self.raster2map(oldsrcx, oldsrcy) srcpt = QgsPoint(newsrcx, newsrcy) self.srcpoints.append(CanvasItem(self.canvas, srcpt)) self.dstpoints.append(CanvasItem(self.iface.mapCanvas(), dstpt)) def savePoints(self): if not self.filename: return filename = self.filename + ".gcps" out = open(filename, "wt") out.write("mapX,mapY,pixelX,pixelY\n") for i in range(len(self.srcpoints)): self.dstpoints[i].setDirty(False) self.srcpoints[i].setDirty(False) dstpt = self.dstpoints[i].pt srcpt = self.srcpoints[i].pt newsrcx, newsrcy = self.map2raster(srcpt.x(), srcpt.y()) outpts = [dstpt.x(), dstpt.y(), newsrcx, newsrcy] out.write("%s,%s,%s,%s\n" % tuple([qgsDoubleToString(x) for x in outpts])) def showMouseCoordinates(self, coords): self.coordslabel.setText("Coords: %f, %f" % (coords.x(), coords.y()))
class Param(QObject): ''' parameter to hold user inputs Attributes ---------- value : basic_type current value of the parameter changed : pyqtSignal fired on change of value ''' changed = pyqtSignal(object) def __init__(self, value, input: InputType = None, label: str = '', unit='', help_text='', repr_format=None, value_label=None): ''' Parameters ---------- value : basic_type initial value of parameter input : InputType, optional input for changing the value in the UI, not settable (and not viewed) in the UI if None label : str, optional label shown when parameter is drawn in UI value_label : str, optional initial label of value shown in UI, defaults to representation of value ''' super().__init__() self._value = value self.label = label self.input = input if self.input: self.input.value = value self.unit = unit self.repr_format = repr_format _repr = value_label if value_label is not None else self._v_repr(value) self._value_label = QLabel(_repr) self.help_text = help_text @property def is_locked(self) -> bool: ''' lock-state of input of the parameter ''' if not self.input: return False return self.input.is_locked @property def value(self) -> object: ''' current value ''' return self._value def _v_repr(self, value: object): ''' formatted string representation of the value ''' if self.repr_format: return locale.format_string(self.repr_format, value) if isinstance(value, float): v_repr = locale.format_string("%.2f", value, grouping=True) elif isinstance(value, bool): v_repr = 'ja' if value == True else 'nein' elif isinstance(value, int): v_repr = f'{value:n}' elif value is None: v_repr = '-' else: v_repr = str(value) return v_repr @value.setter def value(self, value: object): ''' set the value of the parameter, will be applied to the input as well ''' self._value = value self._value_label.setText(self._v_repr(value)) if self.input: self.input.value = value self.changed.emit(value) def draw(self, layout: QLayout, edit: bool = False): ''' draw parameter in given layout Parameters ---------- layout : QBoxLayout layout to append the drawn parameter to edit : bool, optional edit mode displaying the label and input of parameter if True, else label and (uneditable) value, by default False ''' if edit and not self.input: return self.row = QHBoxLayout() label = QLabel(self.label) spacer = QFrame() spacer_layout = QHBoxLayout() spacer_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding)) spacer.setLayout(spacer_layout) # dotted line in preview if not edit: spacer.setFrameShape(QFrame.StyledPanel) spacer.setStyleSheet('border-width: 1px; border-style: none; ' 'border-bottom-style: dotted;' 'border-color: grey;') if isinstance(layout, QGridLayout): n_rows = layout.rowCount() layout.addWidget(label, n_rows, 0) layout.addWidget(spacer, n_rows, 1) layout.addLayout(self.row, n_rows, 2) else: self.row.addWidget(label) self.row.addWidget(spacer) layout.addLayout(self.row) if edit: self.input.draw(self.row, unit=self.unit) else: self.row.addWidget(self._value_label) if self.unit: unit_label = QLabel(self.unit) self.row.addWidget(unit_label)
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()
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 Ui_form1(object): def setupUi(self, form1): form1.setObjectName(_fromUtf8("form1")) form1.resize(400, 253) form1.setFocusPolicy(QtCore.Qt.TabFocus) form1.setWindowTitle(_fromUtf8("Kuwahara filter")) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/qgis.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) form1.setWindowIcon(icon) self.label = QLabel(form1) self.label.setGeometry(QtCore.QRect(21, 10, 111, 20)) font = QtGui.QFont() font.setPointSize(10) self.label.setFont(font) self.label.setToolTip(_fromUtf8("")) self.label.setObjectName(_fromUtf8("label")) self.outputb = QPushButton(form1) self.outputb.setGeometry(QtCore.QRect(320, 47, 31, 23)) self.outputb.setObjectName(_fromUtf8("outputb")) self.label_2 = QLabel(form1) self.label_2.setGeometry(QtCore.QRect(22, 49, 101, 20)) font = QtGui.QFont() font.setPointSize(10) self.label_2.setFont(font) self.label_2.setToolTip(_fromUtf8("")) self.label_2.setObjectName(_fromUtf8("label_2")) self.progressBar = QProgressBar(form1) self.progressBar.setGeometry(QtCore.QRect(19, 220, 361, 23)) self.progressBar.setProperty(_fromUtf8("value"), 24) self.progressBar.setObjectName(_fromUtf8("progressBar")) self.label_3 = QLabel(form1) self.label_3.setGeometry(QtCore.QRect(22, 88, 131, 20)) font = QtGui.QFont() font.setPointSize(10) self.label_3.setFont(font) self.label_3.setObjectName(_fromUtf8("label_3")) self.label_4 = QLabel(form1) self.label_4.setGeometry(QtCore.QRect(21, 125, 181, 20)) font = QtGui.QFont() font.setPointSize(10) self.label_4.setFont(font) self.label_4.setObjectName(_fromUtf8("label_4")) self.run = QPushButton(form1) self.run.setGeometry(QtCore.QRect(139, 185, 101, 23)) self.run.setObjectName(_fromUtf8("run")) self.inputbox = QgsMapLayerComboBox(form1) self.inputbox.setGeometry(QtCore.QRect(141, 10, 170, 22)) self.inputbox.setObjectName(_fromUtf8("input")) self.output = QLineEdit(form1) self.output.setGeometry(QtCore.QRect(149, 45, 160, 28)) self.output.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.output.setObjectName(_fromUtf8("output")) self.refb = QLineEdit(form1) self.refb.setGeometry(QtCore.QRect(149, 82, 160, 28)) self.refb.setObjectName(_fromUtf8("refb")) self.mem = QLineEdit(form1) self.mem.setGeometry(QtCore.QRect(208, 120, 101, 28)) self.mem.setObjectName(_fromUtf8("mem")) self.addout = QCheckBox(form1) self.addout.setGeometry(QtCore.QRect(100, 158, 171, 17)) self.addout.setChecked(True) self.addout.setObjectName(_fromUtf8("checkBox")) self.inputb = QPushButton(form1) self.inputb.setGeometry(QtCore.QRect(320, 10, 31, 23)) self.inputb.setObjectName(_fromUtf8("inputb")) self.retranslateUi(form1) self.setWindowFlags(QtCore.Qt.WindowFlags(QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint | QtCore.Qt.WindowCloseButtonHint)) QtCore.QMetaObject.connectSlotsByName(form1) def retranslateUi(self, form1): self.label.setText(QtCore.QCoreApplication.translate("form1", "Input raster")) self.outputb.setText("...") self.label_2.setText(QApplication.translate("form1", "Output raster")) self.label_3.setToolTip(QApplication.translate("form1", "Reference band from which variances will be calculated to choose subwindow mean.")) self.label_3.setText(QApplication.translate("form1", "Reference band")) self.label_4.setToolTip(QApplication.translate("form1", "Maximum memory usage in megabytes (it is an approximated value, since algorithm will only choose how many lines will be read at once).")) self.label_4.setText(QApplication.translate("form1", "Max memory usage (MB)")) self.run.setText(QApplication.translate("form1", "Run")) self.output.setPlaceholderText(QApplication.translate("form1", "<temporary file>")) self.refb.setToolTip(QApplication.translate("form1", "Reference band from which variances will be calculated to choose subwindow mean.")) self.refb.setText("1") self.mem.setToolTip(QApplication.translate("form1", "Maximum memory usage in MeB (it is an approximated value, since algorithm will only choose how many lines will be read at once).")) self.mem.setText("100") self.addout.setText(QApplication.translate("form1", "Add results to project")) self.inputb.setText("...")
class ProjectFromOSMDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface): QtWidgets.QDialog.__init__(self) self.iface = iface self.setupUi(self) self.path = standard_path() self.error = None self.report = [] self.worker_thread = None self.running = False self.bbox = None self.json = [] self.project = None self.logger = logging.getLogger("aequilibrae") self._run_layout = QGridLayout() # Area to import network for self.choose_place = QRadioButton() self.choose_place.setText("Place name") self.choose_place.toggled.connect(self.change_place_type) self.choose_place.setChecked(False) self.choose_canvas = QRadioButton() self.choose_canvas.setText("Current map canvas area") self.choose_canvas.setChecked(True) self.place = QLineEdit() self.place.setVisible(False) self.source_type_frame = QVBoxLayout() self.source_type_frame.setAlignment(Qt.AlignLeft) self.source_type_frame.addWidget(self.choose_place) self.source_type_frame.addWidget(self.choose_canvas) self.source_type_frame.addWidget(self.place) self.source_type_widget = QGroupBox('Target') self.source_type_widget.setLayout(self.source_type_frame) # Buttons and output self.but_choose_output = QPushButton() self.but_choose_output.setText("Choose file output") self.but_choose_output.clicked.connect(self.choose_output) self.output_path = QLineEdit() self.but_run = QPushButton() self.but_run.setText("Import network and create project") self.but_run.clicked.connect(self.run) self.buttons_frame = QVBoxLayout() self.buttons_frame.addWidget(self.but_choose_output) self.buttons_frame.addWidget(self.output_path) self.buttons_frame.addWidget(self.but_run) self.buttons_widget = QWidget() self.buttons_widget.setLayout(self.buttons_frame) self.progressbar = QProgressBar() self.progress_label = QLabel() self.update_widget = QWidget() self.update_frame = QVBoxLayout() self.update_frame.addWidget(self.progressbar) self.update_frame.addWidget(self.progress_label) self.update_widget.setLayout(self.update_frame) self.update_widget.setVisible(False) self._run_layout.addWidget(self.source_type_widget) self._run_layout.addWidget(self.buttons_widget) self._run_layout.addWidget(self.update_widget) self.setLayout(self._run_layout) self.resize(280, 250) def choose_output(self): new_name, file_type = GetOutputFileName(self, '', ["SQLite database(*.sqlite)"], ".sqlite", self.path) if new_name is not None: self.output_path.setText(new_name) def run(self): self.update_widget.setVisible(True) self.resize(280, 300) if self.choose_canvas.isChecked(): self.report.append( reporter('Chose to download network for canvas area')) e = self.iface.mapCanvas().extent() bbox = [e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum()] else: self.progress_label.setText('Establishing area for download') self.report.append(reporter('Chose to download network for place')) bbox, r = placegetter(self.place.text()) self.report.extend(r) if bbox is None: self.leave() return west, south, east, north = bbox[0], bbox[1], bbox[2], bbox[3] self.report.append( reporter( 'Downloading network for bounding box ({} {}, {}, {})'.format( west, south, east, north))) self.bbox = bbox surveybox = QgsRectangle(QgsPointXY(west, south), QgsPointXY(east, north)) geom = QgsGeometry().fromRect(surveybox) conv = QgsDistanceArea() area = conv.convertAreaMeasurement(conv.measureArea(geom), QgsUnitTypes.AreaSquareMeters) self.report.append( reporter( 'Area for which we will download a network: {:,} km.sq'.format( area / 1000000))) if area <= max_query_area_size: geometries = [[west, south, east, north]] else: parts = math.ceil(area / max_query_area_size) horizontal = math.ceil(math.sqrt(parts)) vertical = math.ceil(parts / horizontal) dx = east - west dy = north - south geometries = [] for i in range(horizontal): xmin = west + i * dx xmax = west + (i + 1) * dx for j in range(vertical): ymin = south + j * dy ymax = south + (j + 1) * dy box = [xmin, ymin, xmax, ymax] geometries.append(box) p = Parameters().parameters modes = [list(k.keys())[0] for k in p['network']['modes']] self.progress_label.setText('Downloading data') self.downloader = OSMDownloader(geometries, modes) self.run_download_thread() def final_steps(self): self.project = Project(self.output_path.text(), True) self.project.network.create_empty_tables() curr = self.project.conn.cursor() curr.execute("""ALTER TABLE links ADD COLUMN osm_id integer""") curr.execute("""ALTER TABLE nodes ADD COLUMN osm_id integer""") self.project.conn.commit() self.project.conn.close() self.builder = OSMBuilder(self.downloader.json, self.project.source) self.run_thread() def run_download_thread(self): self.downloader.downloading.connect(self.signal_downloader_handler) self.downloader.start() self.exec_() def run_thread(self): self.builder.building.connect(self.signal_handler) self.builder.start() self.exec_() def change_place_type(self): if self.choose_place.isChecked(): self.place.setVisible(True) else: self.place.setVisible(False) def leave(self): self.close() dlg2 = ReportDialog(self.iface, self.report) dlg2.show() dlg2.exec_() def signal_downloader_handler(self, val): if val[0] == "Value": self.progressbar.setValue(val[1]) elif val[0] == "maxValue": self.progressbar.setRange(0, val[1]) elif val[0] == "text": self.progress_label.setText(val[1]) elif val[0] == "FinishedDownloading": self.final_steps() def signal_handler(self, val): if val[0] == "Value": self.progressbar.setValue(val[1]) elif val[0] == "maxValue": self.progressbar.setRange(0, val[1]) elif val[0] == "text": self.progress_label.setText(val[1]) elif val[0] == "finished_threaded_procedure": self.project = Project(self.output_path.text()) self.progress_label.setText('Adding spatial indices') self.project.network.add_spatial_index() self.project.network.add_triggers() l = self.project.network.count_links() n = self.project.network.count_nodes() self.report.append(reporter(f'{l:,} links generated')) self.report.append(reporter(f'{n:,} nodes generated')) self.leave()
def construct_form_param_system(self, row, pos): widget = None for field in row[pos]['fields']: if field['label']: lbl = QLabel() lbl.setObjectName('lbl' + field['widgetname']) lbl.setText(field['label']) lbl.setMinimumSize(160, 0) lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) lbl.setToolTip(field['tooltip']) if field['widgettype'] == 'text' or field[ 'widgettype'] == 'linetext': widget = QLineEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'textarea': widget = QTextEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'combo': widget = QComboBox() self.populate_combo(widget, field) widget.currentIndexChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'checkbox' or field[ 'widgettype'] == 'check': widget = QCheckBox() if field['value'].lower() == 'true': widget.setChecked(True) elif field['value'].lower() == 'FALSE': widget.setChecked(False) widget.stateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) elif field['widgettype'] == 'datepickertime': widget = QDateEdit() widget.setCalendarPopup(True) date = QDate.fromString(field['value'], 'yyyy/MM/dd') widget.setDate(date) widget.dateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'spinbox': widget = QDoubleSpinBox() if 'value' in field and field['value'] is not None: value = float(str(field['value'])) widget.setValue(value) widget.valueChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) else: pass if widget: widget.setObjectName(field['widgetname']) else: pass # Order Widgets if field['layout_id'] == 1: self.order_widgets_system(field, self.basic_form, lbl, widget) elif field['layout_id'] == 2: self.order_widgets_system(field, self.om_form, lbl, widget) elif field['layout_id'] == 3: self.order_widgets_system(field, self.workcat_form, lbl, widget) elif field['layout_id'] == 4: self.order_widgets_system(field, self.mapzones_form, lbl, widget) elif field['layout_id'] == 5: self.order_widgets_system(field, self.cad_form, lbl, widget) elif field['layout_id'] == 6: self.order_widgets_system(field, self.epa_form, lbl, widget) elif field['layout_id'] == 7: self.order_widgets_system(field, self.masterplan_form, lbl, widget) elif field['layout_id'] == 8: self.order_widgets_system(field, self.other_form, lbl, widget) elif field['layout_id'] == 9: self.order_widgets_system(field, self.node_type_form, lbl, widget) elif field['layout_id'] == 10: self.order_widgets_system(field, self.cat_form, lbl, widget) elif field['layout_id'] == 11: self.order_widgets_system(field, self.utils_form, lbl, widget) elif field['layout_id'] == 12: self.order_widgets_system(field, self.connec_form, lbl, widget) elif field['layout_id'] == 13: self.order_widgets_system(field, self.topology_form, lbl, widget) elif field['layout_id'] == 14: self.order_widgets_system(field, self.builder_form, lbl, widget) elif field['layout_id'] == 15: self.order_widgets_system(field, self.review_form, lbl, widget) elif field['layout_id'] == 16: self.order_widgets_system(field, self.analysis_form, lbl, widget) elif field['layout_id'] == 17: self.order_widgets_system(field, self.system_form, lbl, widget)
class PlanetOrderItemTypeWidget(QWidget): selectionChanged = pyqtSignal() def __init__(self, item_type, images): super().__init__() self.thumbnails = [] self.item_type = item_type self.images = images layout = QGridLayout() layout.setMargin(0) self.labelThumbnail = QLabel() pixmap = QPixmap(PLACEHOLDER_THUMB, "SVG") thumb = pixmap.scaled(96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.labelThumbnail.setPixmap(thumb) self.labelThumbnail.setFixedSize(96, 96) layout.addWidget(self.labelThumbnail, 0, 0, 3, 1) for image in images: url = f"{image['_links']['thumbnail']}?api_key={PlanetClient.getInstance().api_key()}" download_thumbnail(url, self) labelName = IconLabel( f"<b>{PlanetClient.getInstance().item_types_names()[self.item_type]}</b>", SATELLITE_ICON, ) labelNumItems = IconLabel(f"{len(images)} items", NITEMS_ICON) layout.addWidget(labelNumItems, 0, 1) layout.addWidget(labelName, 1, 1) self.btnDetails = QPushButton() self.btnDetails.setFlat(True) self.btnDetails.setIcon(EXPAND_MORE_ICON) self.btnDetails.clicked.connect(self._btnDetailsClicked) layout.addWidget(self.btnDetails, 0, 2) self.widgetDetails = QWidget() layout.addWidget(self.widgetDetails, 3, 0, 1, 3) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout.addWidget(line, 4, 0, 1, 3) self.setLayout(layout) self.widgetDetails.hide() self.updateGeometry() self.populate_details() def populate_details(self): self.bundleWidgets = [] client = PlanetClient.getInstance() permissions = [img[PERMISSIONS] for img in self.images] item_bundles = client.bundles_for_item_type_and_permissions( self.item_type, permissions=permissions) default = default_bundles.get(self.item_type, []) def _center(obj): hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(obj) hlayout.addStretch() return hlayout layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(20) layout.addLayout(_center(QLabel("<b>RECTIFIED ASSETS</b>"))) gridlayout = QGridLayout() gridlayout.setMargin(0) assets = PlanetClient.getInstance().asset_types_for_item_type( self.item_type) assets_and_bands = {} for a in assets: if "bands" in a: assets_and_bands[a["id"]] = len(a["bands"]) widgets = {} i = 0 for bundleid, bundle in item_bundles.items(): if bundle["rectification"] == "orthorectified": w = PlanetOrderBundleWidget(bundleid, bundle, self.item_type) gridlayout.addWidget(w, i // 2, i % 2) w.setSelected(False) widgets[bundleid] = w w.selectionChanged.connect( partial(self._bundle_selection_changed, w)) self.bundleWidgets.append(w) i += 1 selected = False for defaultid in default: for bundleid, w in widgets.items(): if defaultid == bundleid: w.setSelected(True) selected = True break if selected: break layout.addLayout(gridlayout) self.labelUnrectified = QLabel("<b>UNRECTIFIED ASSETS</b>") layout.addLayout(_center(self.labelUnrectified)) self.widgetUnrectified = QWidget() gridlayoutUnrect = QGridLayout() gridlayoutUnrect.setMargin(0) i = 0 for bundleid, bundle in item_bundles.items(): if bundle["rectification"] != "orthorectified": w = PlanetOrderBundleWidget(bundleid, bundle, self.item_type) gridlayoutUnrect.addWidget(w, i // 2, i % 2) w.selectionChanged.connect( partial(self._bundle_selection_changed, w)) self.bundleWidgets.append(w) i += 1 self.widgetUnrectified.setLayout(gridlayoutUnrect) layout.addWidget(self.widgetUnrectified) self.labelMore = QLabel('<a href="#">+ Show More</a>') self.labelMore.setOpenExternalLinks(False) self.labelMore.setTextInteractionFlags(Qt.LinksAccessibleByMouse) self.labelMore.linkActivated.connect(self._showMoreClicked) layout.addLayout(_center(self.labelMore)) self.widgetUnrectified.hide() self.labelUnrectified.hide() self.widgetDetails.setLayout(layout) def _bundle_selection_changed(self, widget): for w in self.bundleWidgets: if widget != w: w.setSelected(False, False) self.selectionChanged.emit() def _showMoreClicked(self): visible = self.widgetUnrectified.isVisible() self.widgetUnrectified.setVisible(not visible) self.labelUnrectified.setVisible(not visible) if visible: self.labelMore.setText('<a href="#">+ Show More</a>') else: self.labelMore.setText('<a href="#">- Show Less</a>') def expand(self): self.widgetDetails.show() self.btnDetails.setIcon(EXPAND_LESS_ICON) self.updateGeometry() def _btnDetailsClicked(self): if self.widgetDetails.isVisible(): self.widgetDetails.hide() self.btnDetails.setIcon(EXPAND_MORE_ICON) else: self.widgetDetails.show() self.btnDetails.setIcon(EXPAND_LESS_ICON) self.updateGeometry() def bundles(self): bundles = [] for w in self.bundleWidgets: if w.selected(): bundle = {} bundle["id"] = w.bundleid bundle["name"] = w.name bundle["filetype"] = w.filetype() bundle["udm"] = w.udm bundle["rectified"] = w.rectified bundle["canharmonize"] = w.can_harmonize bundle["canclip"] = w.can_clip bundles.append(bundle) return bundles def set_thumbnail(self, img): thumbnail = QPixmap(img) self.thumbnails.append( thumbnail.scaled(96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation)) if len(self.images) == len(self.thumbnails): bboxes = [img[GEOMETRY] for img in self.images] pixmap = createCompoundThumbnail(bboxes, self.thumbnails) thumb = pixmap.scaled(128, 128, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.labelThumbnail.setPixmap(thumb)
def treeItemChanged(self, current, previous): qgsgeom1 = None qgsgeom2 = None crs = "EPSG:4326" if not isinstance(current, FeatureItem): self.attributesTable.clear() self.attributesTable.setRowCount(0) return color = { "MODIFIED": QColor(255, 170, 0), "ADDED": Qt.green, "REMOVED": Qt.red, "NO_CHANGE": Qt.white } path = current.layername + "/" + current.featureid featurediff = self.changes[path].featurediff() self.attributesTable.clear() self.attributesTable.verticalHeader().show() self.attributesTable.horizontalHeader().show() self.attributesTable.setRowCount(len(featurediff)) self.attributesTable.setVerticalHeaderLabels( [a["attributename"] for a in featurediff]) self.attributesTable.setHorizontalHeaderLabels( ["Old value", "New value", "Change type"]) for i, attrib in enumerate(featurediff): try: if attrib["changetype"] == "MODIFIED": oldvalue = attrib["oldvalue"] newvalue = attrib["newvalue"] elif attrib["changetype"] == "ADDED": newvalue = attrib["newvalue"] oldvalue = "" elif attrib["changetype"] == "REMOVED": oldvalue = attrib["oldvalue"] newvalue = "" else: oldvalue = newvalue = attrib["oldvalue"] except: oldvalue = newvalue = "" self.attributesTable.setItem(i, 0, DiffItem(oldvalue)) self.attributesTable.setItem(i, 1, DiffItem(newvalue)) try: self.attributesTable.setItem(i, 2, QTableWidgetItem("")) if qgsgeom1 is None or qgsgeom2 is None: if "crs" in attrib: crs = attrib["crs"] qgsgeom1 = QgsGeometry.fromWkt(oldvalue) qgsgeom2 = QgsGeometry.fromWkt(newvalue) if qgsgeom1 is not None and qgsgeom2 is not None: widget = QWidget() btn = QPushButton() btn.setText("View detail") btn.clicked.connect(lambda: self.viewGeometryChanges( qgsgeom1, qgsgeom2, crs)) label = QLabel() label.setText(attrib["changetype"]) layout = QHBoxLayout(widget) layout.addWidget(label) layout.addWidget(btn) layout.setContentsMargins(0, 0, 0, 0) widget.setLayout(layout) self.attributesTable.setCellWidget(i, 2, widget) else: self.attributesTable.setItem( i, 2, QTableWidgetItem(attrib["changetype"])) else: self.attributesTable.setItem( i, 2, QTableWidgetItem(attrib["changetype"])) except: self.attributesTable.setItem( i, 2, QTableWidgetItem(attrib["changetype"])) for col in range(3): self.attributesTable.item(i, col).setBackgroundColor( color[attrib["changetype"]]) self.attributesTable.resizeColumnsToContents() self.attributesTable.horizontalHeader().setResizeMode( QHeaderView.Stretch)
class VoGISProfilToolPlotDialog(QDialog): def __init__(self, interface, settings, profiles, intersections, cadastre): QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolPlot() self.ui.setupUi(self) self.ui.btnAddLayout.clicked.connect(self.addToLayout) self.iface = interface #output debug Log Messages: mainly for debugging matplotlib self.debug = False self.settings = settings self.profiles = profiles self.intersections = intersections self.cadastre = cadastre if self.settings.onlyHektoMode is True: self.ui.IDC_frPlot.hide() self.ui.IDC_frToolbar.hide() self.adjustSize() self.ui.IDC_chkHekto.setCheckState(Qt.Checked) self.ui.IDC_chkHekto.setEnabled(False) self.filePath = QgsSettings().value("vogisprofiltoolmain/savepath", "") #nimmt die Locale vom System, nicht von QGIS #kein Weg gefunden, um QGIS Locale: QSettings().value("locale/userLocale") #zu initialisieren, nur um Dezimaltrenne auszulesen #QgsMessageLog.logMessage("QGIS Locale:{0}".format(QSettings().value("locale/userLocale").toString()), "VoGis", Qgis.Info) #!!!nl_langinfo not available on Windows!!! #http://docs.python.org/2.7/library/locale.html#locale.nl_langinfo # ... This function is not available on all systems ... #decimalDelimiter = locale.nl_langinfo(locale.RADIXCHAR) decimalDelimiter = locale.localeconv()["decimal_point"] QgsMessageLog.logMessage("delimiter:{0}".format(decimalDelimiter), "VoGis", Qgis.Info) idx = self.ui.IDC_cbDecimalDelimiter.findText(decimalDelimiter, Qt.MatchExactly) QgsMessageLog.logMessage("idx:{0}".format(idx), "VoGis", Qgis.Info) self.ui.IDC_cbDecimalDelimiter.setCurrentIndex(idx) plt_extent = PlotExtent() for profile in self.profiles: plt_extent.union(profile.getExtent()) if self.debug: QgsMessageLog.logMessage(plt_extent.toString(), "VoGis", Qgis.Info) plt_extent.expand() self.orig_plt_xtnt = PlotExtent(plt_extent.xmin, plt_extent.ymin, plt_extent.xmax, plt_extent.ymax) self.plt_widget = self.__createMatplotlibCanvas(plt_extent) layout = self.ui.IDC_frPlot.layout() #QgsMessageLog.logMessage("layout: {0}".format(layout), "VoGis", Qgis.Info) layout.addWidget(self.plt_widget) plt_toolbar = NavigationToolbar2QT(self.plt_widget, self.ui.IDC_frPlot) self.ui.IDC_frToolbar.layout().addWidget(plt_toolbar) #adjust actions #QgsMessageLog.logMessage("{0}".format(dir(lstActions[0])), "VoGis", Qgis.Info) # for a in plt_toolbar.actions(): # QgsMessageLog.logMessage("{0}".format(a.text()), "VoGis", Qgis.Info) # for t in plt_toolbar.toolitems: # QgsMessageLog.logMessage("{0}".format(t), "VoGis", Qgis.Info) #lstActions = plt_toolbar.actions() #https://hub.qgis.org/wiki/17/Icons_20 firstaction = None for a in plt_toolbar.actions(): atxt = a.text() if atxt == "Home": firstaction = a a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/home.png")) elif atxt == "Back": a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/zoomlast.png")) elif atxt == "Forward": a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/zoomnext.png")) elif atxt == "Pan": a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/pan.png")) elif atxt == "Zoom": a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/zoomselect.png")) elif atxt == "Save": a.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/save.png")) else: plt_toolbar.removeAction(a) self.actionSaveScale = QAction(QIcon(":/plugins/vogisprofiltoolmain/icons/save.png"), QApplication.translate("code", "Maßstab speichern"), plt_toolbar) plt_toolbar.addAction(self.actionSaveScale) self.actionSaveScale.triggered.connect(self.saveScale) #insert 1:1 zoom button self.one2one = QPushButton() self.one2one.setText("1:1") self.one2one.clicked.connect(self.__one2oneClicked) #plt_toolbar.addWidget(self.one2one) #insert QLineEdit to change the exaggeration #catch updating of figure, when exaggeration QLineEdit has been updated from draw_event of figure self.draw_event_fired = False #catch closing of dialog, when enter key has been used accept exaggeration edit field self.exaggeration_edited = False #insert edit field for exaggeration self.editExaggeration = QLineEdit() self.editExaggeration.setFixedWidth(60) self.editExaggeration.setMaximumWidth(60) plt_toolbar.insertWidget(firstaction, self.editExaggeration) self.editExaggeration.editingFinished.connect(self.__exaggeration_edited) #insert identify button -> deactivate all tools #HACK: picked event gets fired before click event self.plotpicked = False plt_toolbar.insertWidget(firstaction, self.one2one) self.identify = QPushButton() self.identify.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/identify.png")) self.identify.clicked.connect(self.__identify) plt_toolbar.insertWidget(firstaction, self.identify) #insert identify label to show name of clicked dhm self.dhmLbl = QLabel() plt_toolbar.insertWidget(firstaction, self.dhmLbl) #insert measure tool self.measuring = False self.measure_tool = QPushButton() self.measure_tool.setIcon(QIcon(":/plugins/vogisprofiltoolmain/icons/measure.png")) self.measure_tool.clicked.connect(self.__measure) plt_toolbar.insertWidget(firstaction, self.measure_tool) #show measure results self.click1 = None self.click2 = None self.click1pnt = None self.click2pnt = None self.measure_lbl = QLabel() self.measure_lbl.setText(" ") plt_toolbar.insertWidget(firstaction, self.measure_lbl) self.__drawProfiles(self.subplot) #save inital view in history plt_toolbar.push_current() #select pan tool plt_toolbar.pan() self.plt_toolbar = plt_toolbar #(matplotlib.pyplot).tight_layout(True) #plt.tight_layout() #self.fig.tight_layout() QApplication.restoreOverrideCursor() def accept(self): #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "ACCEPTED") QgsMessageLog.logMessage("accept: {0}".format(self.exaggeration_edited), "VoGis", Qgis.Info) if self.exaggeration_edited is True: self.exaggeration_edited = False return QDialog.accept(self) def reject(self): #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "REJECTED") QgsMessageLog.logMessage("reject: {0}".format(self.exaggeration_edited), "VoGis", Qgis.Info) if self.exaggeration_edited is True: self.exaggeration_edited = False return QDialog.reject(self) def exportShpPnt(self): self.__exportShp(True) def exportShpLine(self): self.__exportShp(False) def __exportShp(self, asPnt): u = Util(self.iface) if asPnt is True: caption = QApplication.translate("code", "Punkt Shapefile exportieren") else: caption = QApplication.translate("code", "Linien Shapefile exportieren") fileName, file_ext = u.getFileName(caption, [["shp", "shp"]], self.filePath) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) expShp = ExportShape(self.iface, (self.ui.IDC_chkHekto.checkState() == Qt.Checked), (self.ui.IDC_chkLineAttributes.checkState() == Qt.Checked), self.__getDelimiter(), self.__getDecimalDelimiter(), fileName, self.settings, self.profiles ) if asPnt is False: expShp.exportLine() else: expShp.exportPoint() def exportCsvXls(self): u = Util(self.iface) caption = QApplication.translate("code", "Excel- oder CSV-Datei exportieren") file_format = [] file_format.append(["Microsoft Excel 2007/2010 XML", "xlsx"]) file_format.append(["Comma-Separated Values CSV", "csv"]) fileName, fileExt = u.getFileName(caption, file_format, self.filePath) QgsMessageLog.logMessage("fileName: {0} fileExt:{1}".format(fileName, fileExt), "VoGis", Qgis.Info) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) hekto = (self.ui.IDC_chkHekto.checkState() == Qt.Checked) attribs = (self.ui.IDC_chkLineAttributes.checkState() == Qt.Checked) delimiter = ";" decimalDelimiter = self.__getDecimalDelimiter() if fileExt == "csv": txt = open(fileName, "w") txt.write(self.profiles[0].writeHeader(self.settings.mapData.rasters.selectedRasters(), hekto, attribs, delimiter)) for p in self.profiles: #txt.write("=====Profil {0}======{1}".format(p.id, os.linesep)) #txt.write("Segments:{0}{1}".format(len(p.segments), os.linesep)) #for s in p.segments: # txt.write("Vertices:{0}{1}".format(len(s.vertices), os.linesep)) txt.write(p.toString(hekto, attribs, delimiter, decimalDelimiter)) else: # BEGIN XLSX-Export exXls = ExportXls(self.iface, fileName, self.settings, self.profiles, hekto, attribs, decimalDelimiter) exXls.create() def exportTxt(self): delimiter = self.__getDelimiter() decimalDelimiter = self.__getDecimalDelimiter() if delimiter == decimalDelimiter: msg = QApplication.translate("code", "Gleiches Dezimal- und Spaltentrennzeichen gewählt!") QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", msg) return u = Util(self.iface) caption = QApplication.translate("code", "Textdatei exportieren") fileName, file_ext = u.getFileName(caption, [["txt", "txt"]], self.filePath) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) hekto = (self.ui.IDC_chkHekto.checkState() == Qt.Checked) attribs = (self.ui.IDC_chkLineAttributes.checkState() == Qt.Checked) txt = open(fileName, "w") txt.write(self.profiles[0].writeHeader(self.settings.mapData.rasters.selectedRasters(), hekto, attribs, delimiter)) for p in self.profiles: #txt.write("=====Profil {0}======{1}".format(p.id, os.linesep)) #txt.write("Segments:{0}{1}".format(len(p.segments), os.linesep)) #for s in p.segments: # txt.write("Vertices:{0}{1}".format(len(s.vertices), os.linesep)) txt.write(p.toString(hekto, attribs, delimiter, decimalDelimiter )) txt.close() def exportAutoCadTxt(self): u = Util(self.iface) caption = QApplication.translate("code", "AutoCad Textdatei exportieren") fileName, file_ext = u.getFileName(caption, [["txt", "txt"]], self.filePath) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) txt = open(fileName, "w") for p in self.profiles: txt.write(p.toACadTxt(" ", ".")) txt.close() def exportDxfPnt(self): self.__exportDxf(True) def exportDxfLine(self): self.__exportDxf(False) def __exportDxf(self, asPnt): u = Util(self.iface) caption = QApplication.translate("code", "DXF exportieren") fileName, file_ext = u.getFileName(caption, [["dxf", "dxf"]], self.filePath) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) exDxf = ExportDxf(self.iface, fileName, self.settings, self.profiles) if asPnt is True: exDxf.exportPoint() else: exDxf.exportLine() def __identify(self): #dirty hack: deselect all tools #selecting a tool twice deselects it self.plt_toolbar.pan() self.plt_toolbar.zoom() self.plt_toolbar.zoom() def __measure(self): self.measuring = True #dirty hack: deselect all tools #selecting a tool twice deselects it self.plt_toolbar.pan() self.plt_toolbar.zoom() self.plt_toolbar.zoom() def __figureDrawn(self, event): if self.debug: QgsMessageLog.logMessage("__figureDrawn, draw_event_fired:{0} exaggeration_edited: {1}".format(self.draw_event_fired, self.exaggeration_edited), "VoGis", Qgis.Info) #draw event seems to get fired twice????? if self.draw_event_fired == True: return self.draw_event_fired = True axes = self.plt_widget.figure.get_axes()[0] xlim = axes.get_xlim() ylim = axes.get_ylim() if self.debug: QgsMessageLog.logMessage("__figureDrawn: xlim:{0} ylim:{1}".format(xlim, ylim), "VoGis", Qgis.Info) dpi = self.plt_widget.figure.get_dpi() fig_width = self.plt_widget.figure.get_figwidth() * dpi fig_height = self.plt_widget.figure.get_figheight() * dpi #bbox = axes.get_window_extent().transformed(self.plt_widget.figure.dpi_scale_trans.inverted()) #fig_width, fig_height = bbox.width * dpi, bbox.height * dpi fig_width *= (TOP_MARGIN - BOTTOM_MARGIN) fig_height *= (RIGHT_MARGIN - LEFT_MARGIN) delta_x = xlim[1] - xlim[0] delta_y = ylim[1] - ylim[0] ratio = (delta_x / fig_width) / (delta_y / fig_height) ratio = floor(ratio * 10) / 10 if self.debug: QgsMessageLog.logMessage("__figureDrawn: fig_width:{0} fig_height:{1} dpi:{2} delta_x:{3} delta_y:{4}, ratio:{5}".format(fig_width, fig_height, dpi, delta_x, delta_y, ratio), "VoGis", Qgis.Info) if self.debug: QgsMessageLog.logMessage("__figureDrawn: axes.get_data_ratio:{0}".format(axes.get_data_ratio()), "VoGis", Qgis.Info) #self.editExaggeration.setText("{0:.1f}".format(ratio)) self.editExaggeration.setText("{0:.1f}".format(axes.get_aspect())) self.draw_event_fired = False def __exaggeration_edited(self, *args): if self.debug: QgsMessageLog.logMessage("__exaggeration_edited, exaggeration_edited:{0} draw_event_fired: {1}".format(self.exaggeration_edited, self.draw_event_fired), "VoGis", Qgis.Info) #this event handler seems to get called twice???? if self.draw_event_fired == True: return if self.exaggeration_edited == True: return self.exaggeration_edited = True #QgsMessageLog.logMessage("__exaggeration_edited: {0}".format(self.exaggeration_edited), "VoGis", Qgis.Info) ut = Util(self.iface) txtExa = QApplication.translate("code", "Überhöhung") if ut.isFloat(self.editExaggeration.text(), txtExa) is False: return False #clear focus of lineedit, otherwise it gets called even when the user wants to close the dialog self.editExaggeration.clearFocus() exa = float(self.editExaggeration.text().replace(",", ".")) self.__adjustAxes(exa) def __one2oneClicked(self): if self.debug: QgsMessageLog.logMessage("1:1 clicked", "VoGis", Qgis.Info) #QgsMessageLog.logMessage("axes:{0}".format(self.plt_widget.figure.get_axes()), "VoGis", Qgis.Info) self.__adjustAxes(1.0) def __adjustAxes(self, exaggeration): # avoid rounding errors for exaggeration exaggerationOrig = exaggeration # one digit after the comma exaggeration = floor(exaggerationOrig * 10) / 10 # two digits after the comma if exaggeration == 0.0: exaggeration = floor(exaggerationOrig * 100) / 100 # three digits after the comma if exaggeration == 0.0: exaggeration == floor(exaggerationOrig * 1000) / 1000 # if still 0 give up and reset to 1 if exaggeration == 0.0: exaggeration = 1.0 axes = self.plt_widget.figure.get_axes()[0] #axes.set_aspect(exaggeration) #axes.set_autoscalex_on(False) #axes.set_autoscaley_on(True) if self.debug: QgsMessageLog.logMessage("__adjustAxes, get_aspect:{0}".format(axes.get_aspect()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, get_position:{0}".format(axes.get_position()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, xBound:{0} xlim:{1}".format(axes.get_xbound(),axes.get_xlim()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, yBound:{0} ylim:{1}".format(axes.get_ybound(),axes.get_ylim()), "VoGis", Qgis.Info) oldexa = axes.get_aspect() ratioexa = oldexa / exaggeration ylim = axes.get_ylim() deltaYold = ylim[1] - ylim[0] deltaYnew = deltaYold * ratioexa centerY = ylim[0] + (deltaYold / 2) axes.set_ylim(centerY - deltaYnew/2,centerY + deltaYnew/2) axes.set_aspect(exaggeration, "datalim", "C") self.plt_widget.draw() if self.debug: QgsMessageLog.logMessage("__adjustAxes, get_aspect:{0}".format(axes.get_aspect()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, get_position:{0}".format(axes.get_position()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, xBound:{0} xlim:{1}".format(axes.get_xbound(),axes.get_xlim()), "VoGis", Qgis.Info) QgsMessageLog.logMessage("__adjustAxes, yBound:{0} ylim:{1}".format(axes.get_ybound(),axes.get_ylim()), "VoGis", Qgis.Info) def __plotPicked(self, event): self.plotpicked = True if self.debug: QgsMessageLog.logMessage("__plotPicked", "VoGis", Qgis.Info) #QgsMessageLog.logMessage("artist:{0}".format(type(event.artist)), "VoGis", Qgis.Info) self.dhmLbl.setText(" ? ") if isinstance(event.artist, Line2D): QgsMessageLog.logMessage("Line2D", "VoGis", Qgis.Info) line = event.artist xdata = line.get_xdata() ydata = line.get_ydata() ind = event.ind QgsMessageLog.logMessage("{0}: {1} {2}".format(ind, xdata, ydata), "VoGis", Qgis.Info) QgsMessageLog.logMessage(help(line), "VoGis", Qgis.Info) elif isinstance(event.artist, LineCollection): QgsMessageLog.logMessage("LineCollection", "VoGis", Qgis.Info) r = self.settings.mapData.rasters.selectedRasters()[event.ind[0]] QgsMessageLog.logMessage("Raster: {0}".format(r.name), "VoGis", Qgis.Info) self.dhmLbl.setText(" [" + r.name + "] ") else: QgsMessageLog.logMessage("no Line2D or LineCollection", "VoGis", Qgis.Info) def __mouse_move(self, event): if self.measuring is False: return if self.click1 is None: return if event.xdata is None or event.ydata is None: return dx = event.xdata - self.click1.xdata dy = event.ydata - self.click1.ydata if self.debug: #QgsMessageLog.logMessage("mouse move name {0}".format(event.name), "VoGis", Qgis.Info) #QgsMessageLog.logMessage("mouse move xdata {0}".format(event.xdata), "VoGis", Qgis.Info) #QgsMessageLog.logMessage("mouse move ydata {0}".format(event.ydata), "VoGis", Qgis.Info) QgsMessageLog.logMessage("dx/dy {0}/{1}".format(dx, dy), "VoGis", Qgis.Info) self.measure_lbl.setText(" x/y:{0:.1f}/{1:.1f} dx/dy:{2:.1f}/{3:.1f}".format( self.click1.xdata, self.click1.ydata, dx, dy )) self.plt_toolbar.draw_rubberband("xy", self.click1.xpixel, self.click1.ypixel, event.x, event.y) def __buttonPressed(self, event): if self.debug: QgsMessageLog.logMessage("__buttonPressed", "VoGis", Qgis.Info) if self.plotpicked is False: self.dhmLbl.setText(" ? ") #reset flag to show "?" next time a button gets pressed self.plotpicked = False if self.measuring is False: return if self.debug: QgsMessageLog.logMessage("{0}".format(dir(event)), "VoGis", Qgis.Info) QgsMessageLog.logMessage("{0}".format(dir(event.xdata)), "VoGis", Qgis.Info) QgsMessageLog.logMessage("{0}".format(dir(event.ydata)), "VoGis", Qgis.Info) QgsMessageLog.logMessage( "x:{0} y:{1} xdata:{2} ydata:{3} click1:{4} click2:{5} click1pnt:{6} click2pnt:{7}".format( event.x, event.y, event.xdata, event.ydata, self.click1, self.click2, self.click1pnt, self.click2pnt), "VoGis", Qgis.Info) QgsMessageLog.logMessage("click1pnt: {0}".format(dir(self.click1pnt)), "VoGis", Qgis.Info) if event.xdata is None or event.ydata is None: return if self.click1 is None: self.click1 = ChartPoint(event.x, event.y, event.xdata, event.ydata) self.click2 = None #self.measure_lbl.setText(u"") self.measure_lbl.setText(" x/y:{0:.1f}/{1:.1f} dx/dy:0/0".format(event.xdata, event.ydata)) if not self.click1pnt is None: p = self.click1pnt.pop(0); p.remove() del p self.click1pnt = None if not self.click2pnt is None: p = self.click2pnt.pop(0); p.remove() del p self.click2pnt = None self.click1pnt = self.subplot.plot(event.xdata, event.ydata, "ro") elif self.click2 is None: self.click2 = ChartPoint(event.x, event.y, event.xdata, event.ydata) #delta_x = abs(self.click2[0] - self.click1[0]) #delta_y = abs(self.click2[1] - self.click1[1]) #dist = ((delta_x ** 2) + (delta_y ** 2)) ** 0.5 #self.measure_lbl.setText(u" dist: {0:.1f} ".format(dist)) delta_x = self.click2.xdata - self.click1.xdata delta_y = self.click2.ydata - self.click1.ydata dist = sqrt(pow(delta_x, 2) + pow(delta_y, 2)) self.measure_lbl.setText(u" dx/dy:{0:.1f}/{1:.1f} d:{2:.1f}".format(delta_x, delta_y, dist)) self.click1 = None self.measuring = False if not self.click2pnt is None: p = self.click2pnt.pop(0); p.remove() del p self.click2pnt = None self.click2pnt = self.subplot.plot(event.xdata, event.ydata, "go") #refresh plot to show points, when identify tool active #if self.debug: QgsMessageLog.logMessage("__buttonPressed: active: {0}".format(self.plt_toolbar._active), "VoGis", Qgis.Info) #if self.plotpicked is True: if self.plt_toolbar._active is None: self.plt_widget.draw() def __createMatplotlibCanvas(self, plt_extent): fig = Figure(#figsize=(1, 1), #tight_layout=True, linewidth=0.0, subplotpars=matplotlib.figure.SubplotParams(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0 ) ) #fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None, hspace=None) #fig = Figure((24, 24), tight_layout=True) # try: # fig = Figure(tight_layout=True) # except: # #tight layout not available # fig = Figure() #fig = plt.figure() #fig.set_tight_layout(True) #font = {"family": "arial", "weight": "normal", "size": 12} #rc("font", **font) rect = fig.patch rect.set_facecolor((0.9, 0.9, 0.9)) # self.subplot = fig.add_axes( # #(0.08, 0.15, 0.92, 0.82), # (0.0, 0.0, 1.0, 1.0), # anchor="SW", # adjustable="box-forced" # ) #left bottom right top self.subplot = fig.add_axes( (LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, TOP_MARGIN), adjustable="datalim", aspect=1 ) #self.subplot.plot.tight_layout(True) self.subplot.set_xbound(plt_extent.xmin, plt_extent.xmax) self.subplot.set_ybound(plt_extent.ymin, plt_extent.ymax) self.__setupAxes(self.subplot) #fig.tight_layout() canvas = FigureCanvasQTAgg(fig) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) canvas.setSizePolicy(sizePolicy) canvas.mpl_connect("pick_event", self.__plotPicked) canvas.mpl_connect("draw_event", self.__figureDrawn) canvas.mpl_connect("button_press_event", self.__buttonPressed) canvas.mpl_connect("motion_notify_event", self.__mouse_move) return canvas def __setupAxes(self, axe1): axe1.grid() axe1.ticklabel_format(style="plain", useOffset=False) axe1.tick_params(axis="both", which="major", direction="out", length=10, width=1, bottom=True, top=False, left=True, right=False ) axe1.minorticks_on() axe1.tick_params(axis="both", which="minor", direction="out", length=5, width=1, bottom=True, top=False, left=True, right=False ) def __getDecimalDelimiter(self): #delim = self.ui.IDC_cbDecimalDelimiter.itemData(self.ui.IDC_cbDecimalDelimiter.currentIndex()) delim = self.ui.IDC_cbDecimalDelimiter.currentText() #QgsMessageLog.logMessage("delim:" + str(delim), "VoGis", Qgis.Info) return delim def __getDelimiter(self): #delim = self.ui.IDC_cbDelimiter.itemData(self.ui.IDC_cbDelimiter.currentIndex()) delim = self.ui.IDC_cbDelimiter.currentText() if delim == "tab": delim = "\t" return delim def addToLayout(self): mgr = QgsProject.instance().layoutManager() layout = None layouts = mgr.layouts() if len(layouts) == 0: QMessageBox.warning(self, QApplication.translate("code", "Keine Layouts"), QApplication.translate("code", "Zuerst ein Layout erstellen")) return elif len(layouts) == 1: layout = layouts[0] else: d = VoGISProfilToolLayoutsDialog(self, layouts) result = d.exec_() if result == QDialog.Accepted: layout = mgr.layoutByName(d.ui.cmbLayouts.currentText()) else: return u = Util(self.iface) caption = QApplication.translate("code", "PNG Datei") file_format = [] file_format.append(["PNG files", "png"]) fileName, fileExt = u.getFileName(caption, file_format, QgsProject.instance().homePath()) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() figure = self.subplot.figure figure.savefig(fileName, format="png") image = QgsLayoutItemPicture(layout) image.setPicturePath(fileName) image.attemptResize(QgsLayoutSize(200, 200)) layout.addLayoutItem(image) def __drawProfiles(self, axes): #for less thatn 10 colors: #alternative method: http://stackoverflow.com/a/14720445 colors = [(1.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 0.0, 1.0, 1.0), (1.0, 1.0, 0.5, 1.0), (1.0, 0.0, 1.0, 1.0), (0.0, 1.0, 1.0, 1.0), (0.415686275, 0.807843137, 0.890196078, 1.0), (0.121568627, 0.470588235, 0.705882353, 1.0), (0.698039216, 0.874509804, 0.541176471, 1.0), (0.2, 0.62745098, 0.17254902, 1.0), (0.984313725, 0.603921569, 0.6, 1.0), (0.890196078, 0.101960784, 0.109803922, 1.0), (0.992156863, 0.749019608, 0.435294118, 1.0), (1, 0.498039216, 0, 1.0), (0.792156863, 0.698039216, 0.839215686, 1.0), (0.415686275, 0.239215686, 0.603921569, 1.0), (1, 1, 0.521568627, 1.0), ] max_profile_length = 0 #idxCol = 0 for idx, p in enumerate(self.profiles): max_profile_length = max(p.getExtent().xmax, max_profile_length) #if idxCol > len(colors) - 1: # idxCol = 0 #x, plt_segments = p.getPlotSegments() #QgsMessageLog.logMessage("x: {0}".format(x), "VoGis", Qgis.Info) plt_segments = p.getPlotSegments() #QgsMessageLog.logMessage("plt_segments: {0}".format(plt_segments), "VoGis", Qgis.Info) lineColl = LineCollection(plt_segments, linewidths=2, linestyles="solid", #colors=colors[randrange(len(colors))], #colors=colors[idxCol], colors=colors[:len(self.settings.mapData.rasters.selectedRasters())], picker=True, label="LBL" ) #lineColl.set_array(x) #lineColl.text.set_text("line label") axes.add_collection(lineColl) #idxCol += 1 #LINE STYLES: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot cmap = cm.get_cmap(cm.gist_rainbow) color_intersection = [cmap(i) for i in numpy.linspace(0, 0.9, len(self.intersections))] for idx, intersection in enumerate(self.intersections): x_1 = intersection.from_dist x_2 = intersection.to_dist z_1 = intersection.from_z[self.settings.intersection_dhm_idx] z_2 = intersection.to_z[self.settings.intersection_dhm_idx] axes.plot( (x_1, x_2), (z_1, z_2), linestyle="--", color=color_intersection[idx], linewidth=4, zorder=1 ) #http://matplotlib.org/users/annotations_guide.html#using-complex-coordinate-with-annotation axes.annotate(u"{0:.2f}".format(z_1), (x_1, z_1), xytext=(0, 30), textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"), ha="center", va="bottom", rotation=90) axes.annotate(u"{0:.2f}".format(z_2), (x_2, z_2), xytext=(0, 30), textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"), ha="center", va="bottom", rotation=90) if self.cadastre is not None: fm = font_manager.FontProperties() s = fm.get_size_in_points() for intersection in self.cadastre: x_1 = intersection.from_dist x_2 = intersection.to_dist axes.plot((x_1, x_1), (0, s), color="black", linewidth=1) axes.plot((x_2, x_2), (0, s), color="black", linewidth=1) def saveScale(self): d = VoGISProfilToolScaleDialog(self) result = d.exec_() if result == QDialog.Rejected: return scale = d.ui.cmbScale.currentText() dpi = d.ui.cmbDpi.currentText() scaleFactor = float(scale.split(":")[1]) crsUnit = self.iface.mapCanvas().mapSettings().destinationCrs().mapUnits() toCm = QgsUnitTypes.fromUnitToUnitFactor(crsUnit, QgsUnitTypes.DistanceCentimeters) m2Cm = QgsUnitTypes.fromUnitToUnitFactor(QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceCentimeters) profile_length = 0 profile_height = 0 plt_extent = PlotExtent() for p in self.profiles: profile_length = max(p.getExtent().xmax, profile_length) profile_height = max(p.getExtent().ymax, profile_height) plt_extent.union(p.getExtent()) plt_extent.expand() s = self.subplot.figure.get_size_inches() maxLength = toCm * profile_length maxHeight = m2Cm * profile_height imgWidth = (maxLength / scaleFactor) * TO_INCH imgHeight = s[1] * imgWidth / s[0] fig = plt.figure(figsize=(imgWidth, imgHeight), dpi=10, linewidth=0.0, subplotpars=matplotlib.figure.SubplotParams(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0 ) ) axes = fig.add_axes((LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, TOP_MARGIN), adjustable="datalim", aspect=1 ) axes.set_xbound(plt_extent.xmin, plt_extent.xmax) axes.set_ybound(plt_extent.ymin, plt_extent.ymax) self.__setupAxes(axes) self.__drawProfiles(axes) exaggeration = floor(float(self.editExaggeration.text().replace(",", ".")) * 10) / 10 oldexa = axes.get_aspect() ratioexa = oldexa / exaggeration ylim = axes.get_ylim() deltaYold = ylim[1] - ylim[0] deltaYnew = deltaYold * ratioexa centerY = ylim[0] + (deltaYold / 2) axes.set_ylim(centerY - deltaYnew/2,centerY + deltaYnew/2) axes.set_aspect(exaggeration, "datalim", "C") axes.set_xlim(self.subplot.get_xlim()) axes.set_ylim(self.subplot.get_ylim()) axes.set_xbound(self.subplot.get_xbound()) axes.set_ybound(self.subplot.get_ybound()) u = Util(self.iface) caption = QApplication.translate("code", "PNG export") file_format = [["PNG files", "png"]] fileName, fileExt = u.getFileName(caption, file_format, self.filePath) if fileName == "": return fInfo = QFileInfo(fileName) self.filePath = fInfo.path() QgsSettings().setValue("vogisprofiltoolmain/savepath", self.filePath) fig.show() fig.savefig(fileName, dpi=int(dpi), format="png", bbox_inches='tight') plt.close(fig)
def postFeatureRequestTaskRun(self): try: self.iface.messageBar().popWidget() except RuntimeError: pass self.iface.messageBar().pushMessage('Lähdeaineiston kohteet haettu', Qgis.Info, duration=5) # self.yleiskaavaDatabase.reconnectToDB() self.shownSourceLayer = self.yleiskaavaSourceDataAPIs.createMemoryLayer( self.originalSourceLayer, self.originalSourceLayerInfo, self.featureRequestTask.features) if self.shownSourceLayer is not None and self.shownSourceLayer.featureCount( ) > 0: self.showSourceLayer() featureCount = 0 featureInfos = [] selectedTargetLayerFeatureRequest = QgsFeatureRequest( ).setNoAttributes().setLimit(1) if self.shownSourceLayer.fields().indexFromName( self.originalSourceLayerInfo["nimi"] ) != -1 and self.shownSourceLayer.fields().indexFromName( self.originalSourceLayerInfo["linkki_data"]) != -1: for index, feature in enumerate( self.shownSourceLayer.getFeatures()): if feature.isValid(): featureCount += 1 # layer.featureCount() ei luotettava featureInfos.append({ "feature": feature, "distance": self.getDistance( self.shownSourceLayer, feature, self.selectedTargetLayer, list( self.selectedTargetLayer.getSelectedFeatures( selectedTargetLayerFeatureRequest))[0]), "nimi": feature[self.originalSourceLayerInfo["nimi"]], "linkki_data": feature[self.originalSourceLayerInfo["linkki_data"]] }) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setRowCount( featureCount) # QgsMessageLog.logMessage('updateTableWidgetSourceTargetMatches - featureCount: ' + str(featureCount), 'Yleiskaava-työkalu', Qgis.Info) if featureCount == 0: self.iface.messageBar().pushMessage( 'Lähdeaineistokarttatasolta ei löytynyt valituista kohteista määritetyn rajaussuorakulmion sisältä kohteita', Qgis.Info, duration=10) for index, featureInfo in enumerate( sorted(featureInfos, key=itemgetter("distance"))): # lähdeaineiston mukaan nimi/tunniste UI:hin # QgsMessageLog.logMessage('updateTableWidgetSourceTargetMatches, nimi: ' + str(featureInfo["nimi"]) + ', linkki_data: ' + str(featureInfo["linkki_data"]), 'Yleiskaava-työkalu', Qgis.Info) userFriendlyFieldNameLabel = QLabel(str(featureInfo["nimi"])) infoLinkButton = QPushButton() infoLinkButton.setText("Näytä lähdetietosivu") infoLinkButton.clicked.connect( partial(self.showInfoPage, str(featureInfo["linkki_data"]))) sourceMapButton = QPushButton() sourceMapButton.setText("Näytä") sourceMapButton.clicked.connect( partial(self.showSourceFeature, self.shownSourceLayer, featureInfo["feature"].id())) distanceLabel = QLabel() distanceLabel.setAlignment(Qt.AlignCenter) distanceLabel.setText(str(featureInfo["distance"])) linkedFeatureWidget = None # yhdistä tietokannan data linkedFeatureID, linkedSourceDataFeature = self.yleiskaavaSourceDataAPIs.getLinkedDatabaseFeatureIDAndSourceDataFeature( self.selectedTargetLayer, featureInfo) if linkedFeatureID != None: linkedFeatureWidget = QPushButton() linkedFeatureWidget.setText("Näytä yhdistetty kohde") linkedFeatureWidget.clicked.connect( partial(self.showLinkedFeature, self.selectedTargetLayer, linkedFeatureID)) else: linkedFeatureWidget = QLabel() linkedFeatureWidget.setAlignment(Qt.AlignCenter) linkedFeatureWidget.setText("-") linkToFeatureButton = QPushButton() linkToFeatureButton.setText("Yhdistä") linkToFeatureButton.clicked.connect( partial( self.addLinkBetweenSourceFeatureAndSelectedTargetFeature, index, featureInfo)) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.FIELD_USER_FRIENDLY_NAME_INDEX, userFriendlyFieldNameLabel) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.INFO_LINK_INDEX, infoLinkButton) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.SOURCE_FEATURE_INDEX, sourceMapButton) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.DISTANCE_INFO_INDEX, distanceLabel) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.LINKED_FEATURE_INDEX, linkedFeatureWidget) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.setCellWidget( index, AddSourceDataLinks.LINK_TO_FEATURE_INDEX, linkToFeatureButton) self.dialogAddSourceDataLinks.tableWidgetSourceTargetMatches.resizeColumnsToContents( )
class ReferencedTableEditor(QWidget): referenced_table_changed = pyqtSignal(str) changed = pyqtSignal() def __init__(self, parent=None): QWidget.__init__(self, parent) self.setupUi() self._block_changed = False self.cbo_ref_table.setInsertPolicy(QComboBox.InsertAlphabetically) self.cbo_ref_table.currentIndexChanged[str].connect(self._on_ref_table_changed) self.cbo_source_field.currentTextChanged.connect(self._on_changed) self.cbo_ref_table.currentIndexChanged[str].connect(self._on_changed) self.cbo_referencing_col.currentTextChanged.connect(self._on_changed) # Tables that will be omitted from the referenced table list self._omit_ref_tables = [] self._layout = None def _on_changed(self): if not self._block_changed: self.changed.emit() def add_omit_table(self, table): """ Add a table name that will be omitted from the list of referenced tables. :param table: Table name that will be omitted :type table: str """ if not table in self._omit_ref_tables: self._omit_ref_tables.append(table) def add_omit_tables(self, tables): """ Add a list of tables that will be omitted from the list of referenced tables. :param tables: Table names to be omitted. :type tables: list """ for t in tables: self.add_omit_table(t) @property def omit_tables(self): """ :return: Returns a list of tables that are to be omitted from the list of referenced tables. """ return self._omit_ref_tables def setupUi(self): self.setObjectName("ReferencedTableEditor") self.gridLayout = QGridLayout(self) self.gridLayout.setVerticalSpacing(10) self.gridLayout.setObjectName("gridLayout") self.label_2 = QLabel(self) self.label_2.setMaximumSize(QSize(100, 16777215)) self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.cbo_source_field = QComboBox(self) self.cbo_source_field.setMinimumSize(QSize(0, 30)) self.cbo_source_field.setObjectName("cbo_source_field") self.gridLayout.addWidget(self.cbo_source_field, 2, 1, 1, 1) self.label = QLabel(self) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 2, 0, 1, 1) self.label_3 = QLabel(self) self.label_3.setObjectName("label_3") self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1) self.cbo_referencing_col = QComboBox(self) self.cbo_referencing_col.setMinimumSize(QSize(0, 30)) self.cbo_referencing_col.setObjectName("cbo_referencing_col") self.gridLayout.addWidget(self.cbo_referencing_col, 3, 1, 1, 1) self.cbo_ref_table = QComboBox(self) self.cbo_ref_table.setMinimumSize(QSize(0, 30)) self.cbo_ref_table.setObjectName("cbo_ref_table") self.gridLayout.addWidget(self.cbo_ref_table, 1, 1, 1, 1) self.label_2.setText(QApplication.translate("ReferencedTableEditor", "References")) self.label.setText(QApplication.translate("ReferencedTableEditor", "Data source field")) self.label_3.setText(QApplication.translate("ReferencedTableEditor", "Referencing")) self._current_profile = current_profile() self._current_profile_tables = [] if self._current_profile is not None: self._current_profile_tables =self._current_profile.user_table_names() # Connect signals QMetaObject.connectSlotsByName(self) self.cbo_ref_table.currentIndexChanged[str].connect(self._load_source_table_fields) def set_layout(self, layout): self._layout = layout self._layout.variablesChanged.connect(self.layout_variables_changed) def layout_variables_changed(self): """ When the user changes the data source then update the fields. """ data_source_name = LayoutUtils.get_stdm_data_source_for_layout(self._layout) self.load_data_source_fields(data_source_name) def on_data_source_changed(self, data_source_name): """ Loads data source fields for the given data source name. """ self.load_data_source_fields(data_source_name) def _on_ref_table_changed(self, table): """ Raise signal when the referenced table changes. :param table: Selected table name. :type table: str """ self.referenced_table_changed.emit(table) def properties(self): """ :returns: Returns the user-defined mapping of linked table and column pairings. :rtype: LinkedTableProps """ l_table = self.cbo_ref_table.currentText() s_field = self.cbo_source_field.currentText() l_field = self.cbo_referencing_col.currentText() return LinkedTableProps(linked_table=l_table, source_field=s_field, linked_field=l_field) def set_properties(self, table_props): """ Sets the combo selection based on the text in the linked table object properties. :param table_props: Object containing the linked table information. :type table_props: LinkedTableProps """ self._block_changed = True GuiUtils.set_combo_current_index_by_text(self.cbo_ref_table, table_props.linked_table) GuiUtils.set_combo_current_index_by_text(self.cbo_source_field, table_props.source_field) GuiUtils.set_combo_current_index_by_text(self.cbo_referencing_col, table_props.linked_field) self._block_changed = False def load_data_source_fields(self, data_source_name): """ Load fields/columns of the given data source. """ if data_source_name == "": self.clear() return columns_names = table_column_names(data_source_name) if len(columns_names) == 0: return self.cbo_source_field.clear() self.cbo_source_field.addItem("") self.cbo_source_field.addItems(columns_names) def clear(self): """ Resets combo box selections. """ self._reset_combo_index(self.cbo_ref_table) self._reset_combo_index(self.cbo_referencing_col) self._reset_combo_index(self.cbo_source_field) def _reset_combo_index(self, combo): if combo.count > 0: combo.setCurrentIndex(0) def reset_referenced_table(self): self._reset_combo_index(self.cbo_ref_table) def load_link_tables(self, reg_exp=None, source=TABLES | VIEWS): self.cbo_ref_table.clear() self.cbo_ref_table.addItem("") ref_tables = [] source_tables = [] # Table source if (TABLES & source) == TABLES: ref_tables.extend(pg_tables(exclude_lookups=True)) for t in ref_tables: # Ensure we are dealing with tables in the current profile if not t in self._current_profile_tables: continue # Assert if the table is in the list of omitted tables if t in self._omit_ref_tables: continue if not reg_exp is None: if reg_exp.indexIn(t) >= 0: source_tables.append(t) else: source_tables.append(t) # View source if (VIEWS & source) == VIEWS: profile_user_views = profile_and_user_views(self._current_profile) source_tables = source_tables + profile_user_views self.cbo_ref_table.addItems(source_tables) def _load_source_table_fields(self, sel): self.cbo_referencing_col.clear() data_source_index = self.cbo_source_field.currentIndex() # self.on_data_source_changed( # self.cbo_source_field.itemData(data_source_index) # ) if not sel: return columns_names = table_column_names(sel) self.cbo_referencing_col.clear() self.cbo_referencing_col.addItem("") self.cbo_referencing_col.addItems(columns_names)
class CommanderWindow(QDialog): def __init__(self, parent, canvas): self.canvas = canvas QDialog.__init__(self, parent, Qt.FramelessWindowHint) self.commands = imp.load_source('commands', self.commandsFile()) self.initGui() def commandsFolder(self): folder = str(os.path.join(userFolder(), 'commander')) mkdir(folder) return os.path.abspath(folder) def commandsFile(self): f = os.path.join(self.commandsFolder(), 'commands.py') if not os.path.exists(f): with open(f, 'w') as out: out.write('from qgis.core import *\n') out.write('import processing\n\n') out.write('def removeall():\n') out.write('\tmapreg = QgsProject.instance()\n') out.write('\tmapreg.removeAllMapLayers()\n\n') out.write('def load(*args):\n') out.write('\tprocessing.load(args[0])\n') return f def algsListHasChanged(self): self.fillCombo() def initGui(self): self.combo = ExtendedComboBox() self.fillCombo() self.combo.setEditable(True) self.label = QLabel('Enter command:') self.errorLabel = QLabel('Enter command:') self.vlayout = QVBoxLayout() self.vlayout.setSpacing(2) self.vlayout.setMargin(0) self.vlayout.addSpacerItem( QSpacerItem(0, OFFSET, QSizePolicy.Maximum, QSizePolicy.Expanding)) self.hlayout = QHBoxLayout() self.hlayout.addWidget(self.label) self.vlayout.addLayout(self.hlayout) self.hlayout2 = QHBoxLayout() self.hlayout2.addWidget(self.combo) self.vlayout.addLayout(self.hlayout2) self.vlayout.addSpacerItem( QSpacerItem(0, OFFSET, QSizePolicy.Maximum, QSizePolicy.Expanding)) self.setLayout(self.vlayout) self.combo.lineEdit().returnPressed.connect(self.run) self.prepareGui() def fillCombo(self): self.combo.clear() # Add algorithms for provider in list(algList.algs.values()): for alg in provider: self.combo.addItem('Processing algorithm: ' + alg) # Add functions for command in dir(self.commands): if isinstance(self.commands.__dict__.get(command), types.FunctionType): self.combo.addItem('Command: ' + command) # Add menu entries menuActions = [] actions = iface.mainWindow().menuBar().actions() for action in actions: menuActions.extend(self.getActions(action)) for action in menuActions: self.combo.addItem('Menu action: ' + str(action.text())) def prepareGui(self): self.combo.setEditText('') self.combo.setMaximumSize( QSize(self.canvas.rect().width() - 2 * OFFSET, ITEMHEIGHT)) self.combo.view().setStyleSheet('min-height: 150px') self.combo.setFocus(Qt.OtherFocusReason) self.label.setMaximumSize(self.combo.maximumSize()) self.label.setVisible(False) self.adjustSize() pt = self.canvas.rect().topLeft() absolutePt = self.canvas.mapToGlobal(pt) self.move(absolutePt) self.resize(self.canvas.rect().width(), HEIGHT) self.setStyleSheet('CommanderWindow {background-color: #e7f5fe; \ border: 1px solid #b9cfe4;}') def getActions(self, action): menuActions = [] menu = action.menu() if menu is None: menuActions.append(action) return menuActions else: actions = menu.actions() for subaction in actions: if subaction.menu() is not None: menuActions.extend(self.getActions(subaction)) elif not subaction.isSeparator(): menuActions.append(subaction) return menuActions def run(self): s = str(self.combo.currentText()) if s.startswith('Processing algorithm: '): algName = s[len('Processing algorithm: '):] alg = algList.getAlgorithm(algName) if alg is not None: self.close() self.runAlgorithm(alg) elif s.startswith("Command: "): command = s[len("Command: "):] try: self.runCommand(command) self.close() except Exception as e: self.label.setVisible(True) self.label.setText('Error:' + str(e)) elif s.startswith('Menu action: '): actionName = s[len('Menu action: '):] menuActions = [] actions = iface.mainWindow().menuBar().actions() for action in actions: menuActions.extend(self.getActions(action)) for action in menuActions: if action.text() == actionName: self.close() action.trigger() return else: try: self.runCommand(s) self.close() except Exception as e: self.label.setVisible(True) self.label.setText('Error:' + str(e)) def runCommand(self, command): tokens = command.split(' ') if len(tokens) == 1: method = self.commands.__dict__.get(command) if method is not None: method() else: raise Exception('Wrong command') else: method = self.commands.__dict__.get(tokens[0]) if method is not None: method(*tokens[1:]) else: raise Exception('Wrong command') def runAlgorithm(self, alg): alg = alg.getCopy() message = alg.checkBeforeOpeningParametersDialog() if message: dlg = MessageDialog() dlg.setTitle(self.tr('Missing dependency')) dlg.setMessage(message) dlg.exec_() return dlg = alg.getCustomParametersDialog() if not dlg: dlg = AlgorithmDialog(alg) canvas = iface.mapCanvas() prevMapTool = canvas.mapTool() dlg.show() dlg.exec_() if canvas.mapTool() != prevMapTool: try: canvas.mapTool().reset() except: pass canvas.setMapTool(prevMapTool)
class WellLogView(QWidget): DEFAULT_COLUMN_WIDTH = 150 def __init__(self, title=None, image_dir=None, parent=None): QWidget.__init__(self, parent) toolbar = QToolBar() self.__log_scene = MyScene(0, 0, 600, 600) self.__log_view = LogGraphicsView(self.__log_scene) self.__log_view.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.__log_scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_column_left = QAction( QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", toolbar) self.__action_move_column_left.triggered.connect( self.on_move_column_left) self.__action_move_column_right = QAction( QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", toolbar) self.__action_move_column_right.triggered.connect( self.on_move_column_right) self.__action_edit_style = QAction( QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_column = QAction( QIcon(os.path.join(image_dir, "add.svg")), "Add a data column", toolbar) self.__action_add_column.triggered.connect(self.on_add_column) self.__action_remove_column = QAction( QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", toolbar) self.__action_remove_column.triggered.connect(self.on_remove_column) #self.__action_move_content_right = QAction("Move content right", toolbar) #self.__action_move_content_left = QAction("Move content left", toolbar) #self.__action_move_content_left.triggered.connect(self.on_move_content_left) #self.__action_move_content_right.triggered.connect(self.on_move_content_right) toolbar.addAction(self.__action_move_column_left) toolbar.addAction(self.__action_move_column_right) toolbar.addAction(self.__action_edit_style) toolbar.addAction(self.__action_add_column) toolbar.addAction(self.__action_remove_column) #self.__toolbar.addAction(self.__action_move_content_left) #self.__toolbar.addAction(self.__action_move_content_right) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(toolbar) vbox.addWidget(self.__log_view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each column self.__columns = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__column_widths = [] self._min_z = 0 self._max_z = 40 self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_column(-1) # by default we have a Z scale self.add_z_scale() def on_rect_changed(self, rect): for item, _ in self.__columns: item.set_height(rect.height()) def set_title(self, title): self.__title_label.setText(title) def _place_items(self): x = 0 for i, c in enumerate(self.__columns): item, legend = c width = self.__column_widths[i] legend.setPos(x, 0) item.setPos(x, legend.boundingRect().height()) x += width self.__log_view.setMinimumSize(x, self.__log_view.minimumSize().height()) def _add_column(self, log_item, legend_item): self.__log_scene.addItem(log_item) self.__log_scene.addItem(legend_item) log_item.set_min_depth(self._min_z) log_item.set_max_depth(self._max_z) self.__columns.append((log_item, legend_item)) self.__column_widths.append(log_item.boundingRect().width()) self._place_items() def _fit_to_max_depth(self): self._min_z = min([ i.min_depth() for i, _ in self.__columns if i.min_depth() is not None ]) self._max_z = max([ i.max_depth() for i, _ in self.__columns if i.max_depth() is not None ]) # if we have only one value, center it on a 2 meters range if self._min_z == self._max_z: self._min_z -= 1.0 self._max_z += 1.0 def _update_column_depths(self): for item, _ in self.__columns: item.set_min_depth(self._min_z) item.set_max_depth(self._max_z) item.update() def add_z_scale(self, title="Depth"): scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2, self.__log_scene.height(), self._min_z, self._max_z) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2, title, unit_of_measure="m") self._add_column(scale_item, legend_item) def remove_data_column(self, data): """Remove data column from widget :param data: data to be removed """ # Column doesn't exist if data not in self.__data2logitems: raise ValueError("Impossible to remove data column : given data" " object doesn't exist") log_item, legend_item = self.__data2logitems[data] for i, (pitem, litem) in enumerate(self.__columns): if pitem == log_item and litem == legend_item: self.__columns.pop(i) self.__column_widths.pop(i) del self.__data2logitems[data] self.__log_scene.removeItem(log_item) self.__log_scene.removeItem(legend_item) return # Columns not found assert False def clear_data_columns(self): # remove item from scenes for (item, legend) in self.__columns: self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal lists self.__columns = [] self.__column_widths = [] self.__data2logitems = {} self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_plot_tooltip(self, txt, station_name=None): if station_name is not None: self.__status_bar.showMessage( u"Station: {} ".format(station_name) + txt) else: self.__status_bar.showMessage(txt) def add_data_column(self, data, title, uom, station_name=None): plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height()), render_type=POLYGON_RENDERER, x_orientation=ORIENTATION_DOWNWARD, y_orientation=ORIENTATION_LEFT_TO_RIGHT) plot_item.set_layer(data.get_layer()) plot_item.tooltipRequested.connect( lambda txt: self.on_plot_tooltip(txt, station_name)) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title, unit_of_measure=uom) data.data_modified.connect( lambda data=data: self._update_data_column(data)) self.__data2logitems[data] = (plot_item, legend_item) self._add_column(plot_item, legend_item) self._update_data_column(data) self._update_column_depths() def _update_data_column(self, data): plot_item, legend_item = self.__data2logitems[data] y_values = data.get_y_values() x_values = data.get_x_values() if y_values is None or x_values is None: plot_item.set_data_window(None) return plot_item.set_data(data.get_x_values(), data.get_y_values()) # r = QRectF(0, min_y, (max_x-min_x)/delta, max_y) # plot_item.set_data_window(r) # legend min_str = "{:.1f}".format(min(data.get_y_values())) max_str = "{:.1f}".format(max(data.get_y_values())) legend_item.set_scale(min_str, max_str) self.__log_scene.update() def add_stratigraphy(self, layer, column_mapping, title): item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), style_file=os.path.join( self.__style_dir, "stratigraphy_style.xml")) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) item.set_layer(layer) item.tooltipRequested.connect(self.on_plot_tooltip) item.set_data( [[f[c] if c is not None else None for c in column_mapping] for f in layer.getFeatures()]) self._add_column(item, legend_item) def add_imagery(self, image_filename, title, depth_from, depth_to): item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), image_filename, depth_from, depth_to) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) self._add_column(item, legend_item) def select_column_at(self, pos): x = pos.x() c = 0 selected = -1 for i, width in enumerate(self.__column_widths): if x >= c and x < c + width: selected = i break c += width self.select_column(selected) def select_column(self, idx): self.__selected_column = idx for i, p in enumerate(self.__columns): item, legend = p item.set_selected(idx == i) legend.set_selected(idx == i) item.update() legend.update() self._update_button_visibility() def selected_column(self): return self.__selected_column def _update_button_visibility(self): idx = self.__selected_column self.__action_move_column_left.setEnabled(idx != -1 and idx > 0) self.__action_move_column_right.setEnabled( idx != -1 and idx < len(self.__columns) - 1) self.__action_edit_style.setEnabled(idx != -1) self.__action_remove_column.setEnabled(idx != -1) def on_move_column_left(self): if self.__selected_column < 1: return sel = self.__selected_column self.__columns[ sel - 1], self.__columns[sel] = self.__columns[sel], self.__columns[sel - 1] self.__column_widths[sel - 1], self.__column_widths[ sel] = self.__column_widths[sel], self.__column_widths[sel - 1] self.__selected_column -= 1 self._place_items() self._update_button_visibility() def on_move_column_right(self): if self.__selected_column == -1 or self.__selected_column >= len( self.__columns) - 1: return sel = self.__selected_column self.__columns[ sel + 1], self.__columns[sel] = self.__columns[sel], self.__columns[sel + 1] self.__column_widths[sel + 1], self.__column_widths[ sel] = self.__column_widths[sel], self.__column_widths[sel + 1] self.__selected_column += 1 self._place_items() self._update_button_visibility() def on_remove_column(self): if self.__selected_column == -1: return sel = self.__selected_column # remove item from scenes item, legend = self.__columns[sel] self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal list del self.__columns[sel] del self.__column_widths[sel] self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_edit_style(self): if self.__selected_column == -1: return item = self.__columns[self.__selected_column][0] item.edit_style() def on_add_column(self): # to be overridden by subclasses pass
class FieldMappingTab(QWidget, object): """Widget class for field mapping.""" def __init__(self, field_group=None, parent=None, iface=None): """Constructor.""" # Init from parent class QWidget.__init__(self, parent) # Attributes self.layer = None self.metadata = {} self.parent = parent self.iface = iface self.field_group = field_group self.setting = QSettings() # TODO(IS): Make dynamic # Main container self.main_layout = QVBoxLayout() # Inner layout self.header_layout = QHBoxLayout() self.content_layout = QHBoxLayout() self.footer_layout = QHBoxLayout() # Header self.header_label = QLabel() self.header_label.setWordWrap(True) # Content self.field_layout = QVBoxLayout() self.parameter_layout = QHBoxLayout() self.field_description = QLabel(tr('List of fields')) self.field_list = QListWidget() self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.field_list.setDragDropMode(QAbstractItemView.DragDrop) self.field_list.setDefaultDropAction(Qt.MoveAction) self.field_list.setSizePolicy( QSizePolicy.Maximum, QSizePolicy.Expanding) # noinspection PyUnresolvedReferences self.field_list.itemSelectionChanged.connect(self.update_footer) # Footer self.footer_label = QLabel() self.footer_label.setWordWrap(True) # Parameters self.extra_parameters = [ (GroupSelectParameter, GroupSelectParameterWidget) ] self.parameters = [] self.parameter_container = None # Adding to layout self.header_layout.addWidget(self.header_label) self.field_layout.addWidget(self.field_description) self.field_layout.addWidget(self.field_list) self.field_layout.setSizeConstraint(QLayout.SetMaximumSize) self.content_layout.addLayout(self.field_layout) self.content_layout.addLayout(self.parameter_layout) self.footer_layout.addWidget(self.footer_label) self.main_layout.addLayout(self.header_layout) self.main_layout.addLayout(self.content_layout) self.main_layout.addLayout(self.footer_layout) self.setLayout(self.main_layout) def set_layer(self, layer, keywords=None): """Set layer and update UI accordingly. :param layer: A vector layer that has been already patched with metadata. :type layer: QgsVectorLayer :param keywords: Custom keyword for the layer. :type keywords: dict, None """ self.layer = layer if keywords is not None: self.metadata = keywords else: # Check if it has keywords if not hasattr(layer, 'keywords'): message = 'Layer {layer_name} does not have keywords.'.format( layer_name=layer.name()) raise KeywordNotFoundError(message) self.metadata = layer.keywords self.populate_parameter() def populate_field_list(self, excluded_fields=None): """Helper to add field of the layer to the list. :param excluded_fields: List of field that want to be excluded. :type excluded_fields: list """ # Populate fields list if excluded_fields is None: excluded_fields = [] self.field_list.clear() for field in self.layer.fields(): # Skip if it's excluded if field.name() in excluded_fields: continue # Skip if it's not number (float, int, etc) if field.type() not in qvariant_numbers: continue field_item = QListWidgetItem(self.field_list) field_item.setFlags( Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) field_item.setData(Qt.UserRole, field.name()) field_item.setText(field.name()) self.field_list.addItem(field_item) def populate_parameter(self): """Helper to setup the parameter widget.""" used_fields = [] self.parameters = [] for field in self.field_group.get('fields', []): selected_option = DO_NOT_REPORT options = OrderedDict([ (DO_NOT_REPORT, { 'label': tr('Do not report'), 'value': None, 'type': STATIC, 'constraint': {} }), ]) # Example: count if field['absolute']: # Used in field options field_label = tr('Count fields') else: # Example: ratio # Used in field options field_label = tr('Ratio fields') global_default_value = get_inasafe_default_value_qsetting( self.setting, GLOBAL, field['key']) options[GLOBAL_DEFAULT] = { 'label': tr('Global default'), 'value': global_default_value, 'type': STATIC, 'constraint': {} } default_custom_value = get_inasafe_default_value_qsetting( self.setting, RECENT, field['key']) custom_value = self.metadata.get( 'inasafe_default_values', {}).get( field['key'], default_custom_value) if field['key'] in self.metadata.get( 'inasafe_default_values', {}): if custom_value == global_default_value: selected_option = GLOBAL_DEFAULT else: selected_option = CUSTOM_VALUE min_value = field['default_value'].get('min_value', 0) max_value = field['default_value'].get('max_value', 100) default_step = (max_value - min_value) / 100.0 step = field['default_value'].get('increment', default_step) options[CUSTOM_VALUE] = { 'label': tr('Custom'), 'value': custom_value, 'type': SINGLE_DYNAMIC, 'constraint': { 'min': min_value, 'max': max_value, 'step': step } } custom_fields = self.metadata.get('inasafe_fields', {}).get( field['key'], []) if field['key'] in self.metadata.get('inasafe_fields', {}): selected_option = FIELDS if isinstance(custom_fields, str): custom_fields = [custom_fields] options[FIELDS] = { 'label': field_label, 'value': custom_fields, 'type': MULTIPLE_DYNAMIC, 'constraint': {} } used_fields.extend(custom_fields) parameter = GroupSelectParameter() parameter.guid = field['key'] parameter.name = field['name'] parameter.options = options parameter.selected = selected_option parameter.help_text = field['help_text'] parameter.description = field['description'] self.parameters.append(parameter) self.parameter_container = ParameterContainer( parameters=self.parameters, extra_parameters=self.extra_parameters, vertical=False ) self.parameter_container.setup_ui() constraints = self.field_group.get('constraints', {}) for key, value in list(constraints.items()): self.parameter_container.add_validator( validators[key], kwargs=value['kwargs'], validation_message=value['message']) self.parameter_layout.addWidget(self.parameter_container) default_ratio_help_text = tr( 'By default, InaSAFE will calculate the default ratio ' 'however users have the option to include this in the ' 'analysis report. If you do not want to see the default ' 'results in the report choose "do not report".') # Set move or copy if self.field_group.get('exclusive', False): # If exclusive, do not add used field. self.populate_field_list(excluded_fields=used_fields) # Use move action since it's exclusive self.field_list.setDefaultDropAction(Qt.MoveAction) # Just make sure that the signal is disconnected try: # noinspection PyUnresolvedReferences self.field_list.itemChanged.disconnect(self.drop_remove) except TypeError: pass # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can only map one field to one concept.') else: # If not exclusive, add all field. self.populate_field_list() # Use copy action since it's not exclusive self.field_list.setDefaultDropAction(Qt.CopyAction) # noinspection PyUnresolvedReferences self.field_list.itemChanged.connect( partial(self.drop_remove, field_list=self.field_list)) self.connect_drop_remove_parameter() # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can map one field to more than one concepts.') self.header_label.setText(header_text) def get_parameter_value(self): """Get parameter of the tab. :returns: Dictionary of parameters by type in this format: {'fields': {}, 'values': {}}. :rtype: dict """ parameters = self.parameter_container.get_parameters(True) field_parameters = {} value_parameters = {} for parameter in parameters: if parameter.selected_option_type() in [SINGLE_DYNAMIC, STATIC]: value_parameters[parameter.guid] = parameter.value elif parameter.selected_option_type() == MULTIPLE_DYNAMIC: field_parameters[parameter.guid] = parameter.value return { 'fields': field_parameters, 'values': value_parameters } def update_footer(self): """Update footer when the field list change.""" field_item = self.field_list.currentItem() if not field_item: self.footer_label.setText('') return field_name = field_item.data(Qt.UserRole) field = self.layer.fields().field(field_name) index = self.layer.fields().lookupField(field_name) unique_values = list(self.layer.uniqueValues(index)) pretty_unique_values = ', '.join([str(v) for v in unique_values[:10]]) footer_text = tr('Field type: {0}\n').format(field.typeName()) footer_text += tr('Unique values: {0}').format(pretty_unique_values) self.footer_label.setText(footer_text) def connect_drop_remove_parameter(self): parameter_widgets = self.parameter_container.get_parameter_widgets() for parameter_widget in parameter_widgets: field_list = parameter_widget.widget().list_widget field_list.itemChanged.connect( partial(self.drop_remove, field_list=field_list)) @staticmethod def drop_remove(*args, **kwargs): """Action when we need to remove dropped item. :param *args: Position arguments. :type *args: list :param kwargs: Keywords arguments. :type kwargs: dict """ dropped_item = args[0] field_list = kwargs['field_list'] num_duplicate = 0 for i in range(field_list.count()): if dropped_item.text() == field_list.item(i).text(): num_duplicate += 1 if num_duplicate > 1: # Notes(IS): For some reason, removeItemWidget is not working. field_list.takeItem(field_list.row(dropped_item))
class FeatureFormWidget(Ui_Form, QWidget): # Raise the cancel event, takes a reason and a level canceled = pyqtSignal(str, int) featuresaved = pyqtSignal() featuredeleted = pyqtSignal() def __init__(self, parent=None): super(FeatureFormWidget, self).__init__(parent) self.setupUi(self) utils.install_touch_scroll(self.scrollArea) toolbar = QToolBar() size = QSize(48, 48) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionDelete = toolbar.addAction(QIcon(":/icons/delete"), "Delete") self.actionDelete.triggered.connect(self.delete_feature) label = '<b style="color:red">*</b> Required fields' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) titlespacer = QWidget() titlespacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(titlespacer) self.titlellabel = QLabel(label) self.titlellabel.setProperty("headerlabel", True) self.titlelabelaction = toolbar.addWidget(self.titlellabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(40) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) self.actionCancel = toolbar.addAction(QIcon(":/icons/cancel"), "Cancel") toolbar.addWidget(spacer2) self.actionSave = toolbar.addAction(QIcon(":/icons/save"), "Save") self.actionSave.triggered.connect(self.save_feature) self.layout().setContentsMargins(0, 3, 0, 3) self.layout().insertWidget(0, toolbar) self.actiontoolbar = QToolBar() self.actiontoolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.actiontoolbar.addWidget(spacer) self.layout().insertWidget(1, self.actiontoolbar) self.featureform = None self.values = {} self.config = {} self.feature = None def set_featureform(self, featureform): """ Note: There can only be one feature form. If you need to show another one make a new FeatureFormWidget """ self.featureform = featureform self.titlellabel.setText(self.featureform.windowTitle()) self.featureform.formvalidation.connect(self._update_validation) self.featureform.helprequest.connect( functools.partial(RoamEvents.helprequest.emit, self)) self.featureform.showlargewidget.connect(RoamEvents.show_widget.emit) self.featureform.enablesave.connect(self.actionSave.setEnabled) self.featureform.enablesave.connect(self.actionSave.setVisible) self.featureform.rejected.connect(self.canceled.emit) self.featureform.accepted.connect(self.featuresaved) actions = self.featureform.form_actions() if actions: for action in actions: self.actiontoolbar.addAction(action) else: self.actiontoolbar.hide() self.featureform.setContentsMargins(0, 0, 0, 0) self.featureformarea.layout().addWidget(self.featureform) def delete_feature(self): try: msg = self.featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg) if not box.exec_(): return try: self.featureform.delete() except featureform.DeleteFeatureException as ex: RoamEvents.raisemessage(*ex.error) self.featureform.featuredeleted(self.feature) self.featuredeleted.emit() def feature_saved(self): self.featuresaved.emit() RoamEvents.featuresaved.emit() def save_feature(self): try: self.featureform.save() except featureform.MissingValuesException as ex: RoamEvents.raisemessage(*ex.error) return except featureform.FeatureSaveException as ex: RoamEvents.raisemessage(*ex.error) self.feature_saved() def set_config(self, config): self.config = config editmode = config['editmode'] allowsave = config.get('allowsave', True) self.feature = config.get('feature', None) tools = config.get('tools', []) candelete = True if tools: candelete = "delete" in tools self.featureform.feature = self.feature self.featureform.editingmode = editmode self.actionDelete.setVisible(editmode and candelete) self.actionSave.setEnabled(allowsave) def _update_validation(self, passed): # Show the error if there is missing fields self.missingfieldaction.setVisible(not passed) def bind_values(self, values): self.values = values self.featureform.bindvalues(values) def after_load(self): self.featureform.loaded() def before_load(self): self.featureform.load(self.config['feature'], self.config['layers'], self.values)
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.snapping = True icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.defaultextent = None self.current_form = None self.last_form = None self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.snappingutils = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snappingutils) threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) smallmode = roam.config.settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.gpsMarker = GPSMarker(self.canvas) self.gpsMarker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(88, 64, 173, 50)) self.currentfeatureband.setOutlineColour(QColor(88, 64, 173)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.refresh_map.connect(self.refresh_map) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.gpslabelposition = QLabel("") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addWidget(self.gpslabelposition) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.update_status_label) self.canvas.scaleChanged.connect(self.update_status_label) self.connectButtons() scalebar_enabled = roam.config.settings.get('scale_bar', False) self.scalebar_enabled = False if scalebar_enabled: roam.utils.warning( "Unsupported feature: Scale bar support not ported to QGIS 3 API yet." ) RoamEvents.raisemessage( "Unsupported feature", "Scale bar support not ported to QGIS 3 API yet", level=RoamEvents.CRITICAL) self.scalebar_enabled = False # self.scalebar = ScaleBarItem(self.canvas) # self.canvas.scene().addItem(self.scalebar) def clear_plugins(self) -> None: """ Clear all the plugin added toolbars from the map interface. """ toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): toolbar.unload() self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames) -> None: """ Add the given plugins to to the mapping interface. Adds the toolbars the plugin exposes as new toolbars for the user. :param pluginnames: The names of the plugins to load. Must already be loaded by the plugin loader """ for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning( "No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): """ Load the plugin toolbars into the mapping interface. :param toolbars: The list of toolbars class objects to load. :return: """ for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ self.snapping = snapping if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ self.snapping = not self.snapping try: self.canvas.mapTool().toggle_snapping() except AttributeError: pass RoamEvents.snappingChanged.emit(self.snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_fixed_label(self, fixed, gpsinfo): if not fixed: self.gpslabel.setText("GPS: Acquiring fix") self.gpslabelposition.setText("") quality_mappings = { 0: "invalid", 1: "GPS", 2: "DGPS", 3: "PPS", 4: "Real Time Kinematic", 5: "Float RTK", 6: "Estimated", 7: "Manual input mode", 8: "Simulation mode" } def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ if not self.gps.connected: return fixtype = self.quality_mappings.get(gpsinfo.quality, "") self.gpslabel.setText( "DOP P:<b>{0:.2f}</b> H:<b>{1:.2f}</b> V:<b>{2:.2f}</b> " "Fix: <b>{3}</b> " "Sats: <b>{4}</b> ".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop, fixtype, gpsinfo.satellitesUsed)) places = roam.config.settings.get("gpsplaces", 8) self.gpslabelposition.setText("X: <b>{x:.{places}f}</b> " "Y: <b>{y:.{places}f}</b> " "Z: <b>{z}m</b> ".format( x=position.x(), y=position.y(), z=gpsinfo.elevation, places=places)) def gps_disconnected(self): self.gpslabel.setText("GPS: Not Active") self.gpslabelposition.setText("") self.gpsMarker.hide() def zoom_to_feature(self, feature): """ Zoom to the given feature in the map. :param feature: :return: """ box = feature.geometry().boundingBox() xmin, xmax, ymin, ymax = box.xMinimum(), box.xMaximum(), box.yMinimum( ), box.yMaximum() xmin -= 5 xmax += 5 ymin -= 5 ymax += 5 box = QgsRectangle(xmin, ymin, xmax, ymax) self.canvas.setExtent(box) self.canvas.refresh() def update_status_label(self, *args) -> None: """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format( extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self) -> None: """ Refresh the map """ self.canvas.refresh() def updatescale(self) -> None: """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) @property def crs(self) -> QgsCoordinateReferenceSystem: """ Get the CRS used that is being used in the canvas :return: The QgsCoordinateReferenceSystem that is used by the canvas """ return self.canvas.mapSettings().destinationCrs() def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.items(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.values(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.currentLayer().startEditing() self.canvas.mapTool().setEditMode(True, feature.geometry(), feature) break self.editfeaturestack.append((form, feature)) self.save_current_form() self.load_form(form) trigger_default_action() def save_current_form(self): self.last_form = self.current_form def restore_last_form(self): self.load_form(self.last_form) def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() if hasattr(tool, "clearBand"): tool.clearBand() self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging smallmode = settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsfixed.connect(self.update_gps_fixed_label) self.gps.gpsposition.connect(self.update_gps_label) self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) self.gpsMarker.setgps(self.gps) self.actionGPS.setgps(gps) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.gpsMarker.show() self.gpsMarker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.snappingutils.setConfig(QgsProject.instance().snappingConfig()) self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = roam.api.utils.layers() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry( "Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry( "Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = [ "1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100" ] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() red = QgsProject.instance().readNumEntry("Gui", "/CanvasColorRedPart", 255)[0] green = QgsProject.instance().readNumEntry("Gui", "/CanvasColorGreenPart", 255)[0] blue = QgsProject.instance().readNumEntry("Gui", "/CanvasColorBluePart", 255)[0] myColor = QColor(red, green, blue) self.canvas.setCanvasColor(myColor) self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(self.snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = QgsMapToolPan(self.canvas) self.infoTool = InfoTool(self.canvas) self.infoTool.setAction(self.actionInfo) self.zoomInTool.setAction(self.actionZoom_In) self.zoomOutTool.setAction(self.actionZoom_Out) self.panTool.setAction(self.actionPan) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggle_raster_layers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ if self.defaultextent: self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture( form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clear_capture_tools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) self.current_form = form def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ tool = form.getMaptool()(self.canvas, form.settings) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(self.show_invalid_geometry_message) def show_invalid_geometry_message(self, message) -> None: """ Shows the message to the user if the there is a invalid geometry capture. :param message: The message to show the user. """ RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL) if self.canvas.currentLayer() is not None: self.canvas.currentLayer().rollBack() RoamEvents.editgeometry_invalid.emit() def add_new_feature(self, form, geometry: QgsGeometry): """ Add a new new feature to the given layer :param form: The form to use for the new feature. :param geometry: The new geometry to create the feature for. """ # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if geometry.isMultipart(): geometry.convertToMultiType() # Transform the new geometry back into the map layers geometry if it's needed transform = self.canvas.mapSettings().layerTransform(layer) if transform.isValid(): geometry.transform(transform, QgsCoordinateTransform.ReverseTransform) try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None, None) self.restore_last_form() def clear_capture_tools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggle_raster_layers(self) -> None: """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh dlg = PickActionDialog(msg="Raster visibility") actions = [ (":/icons/raster_0", "Off", partial(self._set_basemaps_opacity, 0), "photo_off"), (":/icons/raster_25", "25%", partial(self._set_basemaps_opacity, .25), "photo_25"), (":/icons/raster_50", "50%", partial(self._set_basemaps_opacity, .50), "photo_50"), (":/icons/raster_75", "75%", partial(self._set_basemaps_opacity, .75), "photo_75"), (":/icons/raster_100", "100%", partial(self._set_basemaps_opacity, 1), "photo_100"), ] # ":/icons/raster_100"), "100%", self, triggered=partial(self._set_raster_layer_value, 1), # objectName="photo_100") dialog_actions = [] for action in actions: icon = QIcon(action[0]) qaction = QAction(icon, action[1], self, triggered=action[2], objectName=action[3]) dialog_actions.append(qaction) dlg.addactions(dialog_actions) dlg.exec_() def _set_basemaps_opacity(self, value=0) -> None: """ Set the opacity for all basemap raster layers. :param value: The opacity value betwen 0 and 1 """ tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): layer = node.layer() if node.layer().type() == QgsMapLayer.RasterLayer: if value > 0: node.setItemVisibilityChecked(Qt.Checked) renderer = layer.renderer() renderer.setOpacity(value) if value == 0: node.setItemVisibilityChecked(Qt.Unchecked) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ # TODO Review cleanup # self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clear_capture_tools() for action in self.layerbuttons: self.editgroup.removeAction(action)
class GeometryProperty(QDialog): def __init__(self, parent): QDialog.__init__(self, parent) # add control to the dialog self.label = QLabel() self.label.setText( QApplication.translate("GeometryProperty", "Select Geometry Type")) self.comboField = QComboBox() self.sridButton = QPushButton() self.sridButton.setText( QApplication.translate("GeometryProperty", "Select Coordinate System ")) self.textField = QLineEdit() setCollectiontypes(geometry_collections, self.comboField) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.addButton(QDialogButtonBox.Cancel) self.sridButton.clicked.connect(self.projectionsSettings) layout = QGridLayout() layout.addWidget(self.label) layout.addWidget(self.comboField) layout.addWidget(self.sridButton) layout.addWidget(self.textField) layout.addWidget(self.buttons) self.setLayout(layout) self.setWindowTitle( QApplication.translate("GeometryProperty", "Geometry Column Property")) self.buttons.accepted.connect(self.setGeometrySetting) self.buttons.rejected.connect(self.cancel) def projectionsSettings(self): '''let user select the projections for the data''' projSelect = ProjectionSelector(self) projection = projSelect.loadAvailableSystems() self.textField.setText(str(projection)) def setGeometrySetting(self): if self.textField.text() == '': self.ErrorInfoMessage( QApplication.translate("GeometryProperty", "Projections is not selected")) return self.value = self.textField.text()[5:] geomType = UserData(self.comboField) self.geomCollection = [self.value, geomType] self.accept() def cancel(self): self.close() def ErrorInfoMessage(self, Message): # Error Message Box msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle( QApplication.translate("GeometryProperty", "Geometry Settings")) msg.setText(Message) msg.exec_()
class DistanceInputPanel(NumberInputPanel): """ Distance input panel for use outside the modeler - this input panel contains a label showing the distance unit. """ def __init__(self, param): super().__init__(param) self.label = QLabel('') self.units_combo = QComboBox() self.base_units = QgsUnitTypes.DistanceUnknownUnit for u in (QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceKilometers, QgsUnitTypes.DistanceFeet, QgsUnitTypes.DistanceMiles, QgsUnitTypes.DistanceYards): self.units_combo.addItem(QgsUnitTypes.toString(u), u) label_margin = self.fontMetrics().width('X') self.layout().insertSpacing(1, label_margin / 2) self.layout().insertWidget(2, self.label) self.layout().insertWidget(3, self.units_combo) self.layout().insertSpacing(4, label_margin / 2) self.warning_label = QLabel() icon = QgsApplication.getThemeIcon('mIconWarning.svg') size = max(24, self.spnValue.height() * 0.5) self.warning_label.setPixmap( icon.pixmap(icon.actualSize(QSize(size, size)))) self.warning_label.setToolTip( self. tr('Distance is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results.' )) self.layout().insertWidget(4, self.warning_label) self.layout().insertSpacing(5, label_margin) self.setUnits(QgsUnitTypes.DistanceUnknownUnit) def setUnits(self, units): self.label.setText(QgsUnitTypes.toString(units)) if QgsUnitTypes.unitType(units) != QgsUnitTypes.Standard: self.units_combo.hide() self.label.show() else: self.units_combo.setCurrentIndex(self.units_combo.findData(units)) self.units_combo.show() self.label.hide() self.warning_label.setVisible(units == QgsUnitTypes.DistanceDegrees) self.base_units = units def setUnitParameterValue(self, value): units = QgsUnitTypes.DistanceUnknownUnit layer = self.getLayerFromValue(value) if isinstance(layer, QgsMapLayer): units = layer.crs().mapUnits() elif isinstance(value, QgsCoordinateReferenceSystem): units = value.mapUnits() elif isinstance(value, str): crs = QgsCoordinateReferenceSystem(value) if crs.isValid(): units = crs.mapUnits() self.setUnits(units) def getValue(self): val = super().getValue() if isinstance(val, float) and self.units_combo.isVisible(): display_unit = self.units_combo.currentData() return val * QgsUnitTypes.fromUnitToUnitFactor( display_unit, self.base_units) return val def setValue(self, value): try: self.spnValue.setValue(float(value)) except: return
class WellLogView(QWidget): DEFAULT_COLUMN_WIDTH = 150 # Emitted when some styles have been updated styles_updated = pyqtSignal() def __init__(self, title=None,image_dir=None, parent=None): QWidget.__init__(self, parent) self.toolbar = QToolBar() self.__log_scene = MyScene(0, 0, 600, 600) self.__log_view = LogGraphicsView(self.__log_scene) self.__log_view.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.__log_scene.sceneRectChanged.connect(self.on_rect_changed) if image_dir is None: image_dir = os.path.join(os.path.dirname(__file__), "img") self.__action_move_column_left = QAction(QIcon(os.path.join(image_dir, "left.svg")), "Move the column to the left", self.toolbar) self.__action_move_column_left.triggered.connect(self.on_move_column_left) self.__action_move_column_right = QAction(QIcon(os.path.join(image_dir, "right.svg")), "Move the column to the right", self.toolbar) self.__action_move_column_right.triggered.connect(self.on_move_column_right) self.__action_edit_style = QAction(QIcon(os.path.join(image_dir, "symbology.svg")), "Edit column style", self.toolbar) self.__action_edit_style.triggered.connect(self.on_edit_style) self.__action_add_column = QAction(QIcon(os.path.join(image_dir, "add.svg")), "Add a data column from configured ones", self.toolbar) self.__action_add_column.triggered.connect(self.on_add_column) self.__action_remove_column = QAction(QIcon(os.path.join(image_dir, "remove.svg")), "Remove the column", self.toolbar) self.__action_remove_column.triggered.connect(self.on_remove_column) #self.__action_move_content_right = QAction("Move content right", self.toolbar) #self.__action_move_content_left = QAction("Move content left", self.toolbar) #self.__action_move_content_left.triggered.connect(self.on_move_content_left) #self.__action_move_content_right.triggered.connect(self.on_move_content_right) self.toolbar.addAction(self.__action_move_column_left) self.toolbar.addAction(self.__action_move_column_right) self.toolbar.addAction(self.__action_edit_style) self.toolbar.addAction(self.__action_add_column) self.toolbar.addAction(self.__action_remove_column) #self.__toolbar.addAction(self.__action_move_content_left) #self.__toolbar.addAction(self.__action_move_content_right) self.__title_label = QLabel() if title is not None: self.set_title(title) self.__status_bar = QStatusBar() vbox = QVBoxLayout() vbox.addWidget(self.__title_label) vbox.addWidget(self.toolbar) vbox.addWidget(self.__log_view) vbox.addWidget(self.__status_bar) self.setLayout(vbox) self.__station_id = None # (log_item, legend_item) for each column self.__columns = [] # { layer : (log_item, legend_item) } self.__data2logitems = {} self.__column_widths = [] self._min_z = 0 self._max_z = 40 self.__allow_mouse_translation = True self.__translation_orig = None self.__style_dir = os.path.join(os.path.dirname(__file__), 'styles') self.select_column(-1) # by default we have a Z scale self.add_z_scale() def on_rect_changed(self, rect): for item, _ in self.__columns: item.set_height(rect.height()) def set_title(self, title): self.__title_label.setText(title) def _place_items(self): x = 0 for i, c in enumerate(self.__columns): item, legend = c width = self.__column_widths[i] legend.setPos(x, 0) item.setPos(x, legend.boundingRect().height()) x += width rect = self.__log_scene.sceneRect() rect.setWidth(x) self.__log_scene.setSceneRect(rect) def _add_column(self, log_item, legend_item): self.__log_scene.addItem(log_item) self.__log_scene.addItem(legend_item) log_item.set_min_depth(self._min_z) log_item.set_max_depth(self._max_z) self.__columns.append((log_item, legend_item)) self.__column_widths.append(log_item.boundingRect().width()) self._place_items() def _fit_to_max_depth(self): self._min_z = min([i.min_depth() for i, _ in self.__columns if i.min_depth() is not None]) self._max_z = max([i.max_depth() for i, _ in self.__columns if i.max_depth() is not None]) # if we have only one value, center it on a 2 meters range if self._min_z == self._max_z: self._min_z -= 1.0 self._max_z += 1.0 def _update_column_depths(self): for item, _ in self.__columns: item.set_min_depth(self._min_z) item.set_max_depth(self._max_z) item.update() def add_z_scale(self, title="Depth"): scale_item = ZScaleItem(self.DEFAULT_COLUMN_WIDTH / 2, self.__log_scene.height(), self._min_z, self._max_z) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH / 2, title, unit_of_measure="m") self._add_column(scale_item, legend_item) def remove_data_column(self, data): """Remove data column from widget :param data: data to be removed """ # Column doesn't exist if data not in self.__data2logitems: raise ValueError("Impossible to remove data column : given data" " object doesn't exist") log_item, legend_item = self.__data2logitems[data] for i, (pitem, litem) in enumerate(self.__columns): if pitem == log_item and litem == legend_item: self.__columns.pop(i) self.__column_widths.pop(i) del self.__data2logitems[data] self.__log_scene.removeItem(log_item) self.__log_scene.removeItem(legend_item) return # Columns not found assert False def clear_data_columns(self): # remove item from scenes for (item, legend) in self.__columns: self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal lists self.__columns = [] self.__column_widths = [] self.__data2logitems = {} self.__selected_column = -1 self._place_items() self._update_button_visibility() # still add z scale self.add_z_scale() def on_plot_tooltip(self, txt, station_name = None): if station_name is not None: self.__status_bar.showMessage(u"Station: {} ".format(station_name) + txt) else: self.__status_bar.showMessage(txt) def add_data_column(self, data, title, uom, station_name = None, config = None): """ Parameters ---------- data: ?? title: str uom: str Unit of measure station_name: str Station name config: PlotConfig """ symbology, symbology_type = config.get_symbology() plot_item = PlotItem(size=QSizeF(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height()), render_type = POLYGON_RENDERER if symbology_type is None else symbology_type, symbology = symbology, x_orientation = ORIENTATION_DOWNWARD, y_orientation = ORIENTATION_LEFT_TO_RIGHT) plot_item.style_updated.connect(self.styles_updated) plot_item.set_layer(data.get_layer()) plot_item.tooltipRequested.connect(lambda txt: self.on_plot_tooltip(txt, station_name)) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title, unit_of_measure=uom) data.data_modified.connect(lambda data=data : self._update_data_column(data, config)) self.__data2logitems[data] = (plot_item, legend_item) self._add_column(plot_item, legend_item) self._update_data_column(data, config) self._update_column_depths() def _update_data_column(self, data, config): plot_item, legend_item = self.__data2logitems[data] y_values = data.get_y_values() x_values = data.get_x_values() if y_values is None or x_values is None: plot_item.set_data_window(None) return plot_item.set_data(data.get_x_values(), data.get_y_values()) win = plot_item.data_window() min_x, min_y, max_x, max_y = win.left(), win.top(), win.right(), win.bottom() if config and config.get('min') is not None: min_y = float(config['min']) if config and config.get('max') is not None: max_y = float(config['max']) # legend legend_item.set_scale(min_y, max_y) plot_item.set_data_window(QRectF(min_x, min_y, max_x-min_x, max_y-min_y)) self.__log_scene.update() def add_stratigraphy(self, layer, filter_expression, column_mapping, title, style_file=None, config=None): """Add stratigraphy data Parameters ---------- layer: QgsVectorLayer The layer where stratigraphic data are stored filter_expression: str A QGIS expression to filter the vector layer column_mapping: dict Dictionary of column names title: str Title of the graph style_file: str Name of the style file to use config: PlotConfig """ symbology = config.get_symbology()[0] if config else None item = StratigraphyItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), style_file=style_file if not symbology else None, symbology=symbology, column_mapping=column_mapping ) item.style_updated.connect(self.styles_updated) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) item.set_layer(layer) item.tooltipRequested.connect(self.on_plot_tooltip) req = QgsFeatureRequest() req.setFilterExpression(filter_expression) item.set_data(list(layer.getFeatures(req))) self._add_column(item, legend_item) def add_imagery(self, image_filename, title, depth_from, depth_to): item = ImageryDataItem(self.DEFAULT_COLUMN_WIDTH, self.__log_scene.height(), image_filename, depth_from, depth_to) legend_item = LegendItem(self.DEFAULT_COLUMN_WIDTH, title) self._add_column(item, legend_item) def select_column_at(self, pos): x = pos.x() c = 0 selected = -1 for i, width in enumerate(self.__column_widths): if x >= c and x < c + width: selected = i break c += width self.select_column(selected) def select_column(self, idx): self.__selected_column = idx for i, p in enumerate(self.__columns): item, legend = p item.set_selected(idx == i) legend.set_selected(idx == i) item.update() legend.update() self._update_button_visibility() def selected_column(self): return self.__selected_column def _update_button_visibility(self): idx = self.__selected_column self.__action_move_column_left.setEnabled(idx != -1 and idx > 0) self.__action_move_column_right.setEnabled(idx != -1 and idx < len(self.__columns) - 1) item = self.__columns[idx][0] if idx > 0 else None self.__action_edit_style.setEnabled(bool(item and not isinstance(item, ImageryDataItem))) self.__action_remove_column.setEnabled(idx != -1) def on_move_column_left(self): if self.__selected_column < 1: return sel = self.__selected_column self.__columns[sel-1], self.__columns[sel] = self.__columns[sel], self.__columns[sel-1] self.__column_widths[sel-1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel-1] self.__selected_column -= 1 self._place_items() self._update_button_visibility() def on_move_column_right(self): if self.__selected_column == -1 or self.__selected_column >= len(self.__columns) - 1: return sel = self.__selected_column self.__columns[sel+1], self.__columns[sel] = self.__columns[sel], self.__columns[sel+1] self.__column_widths[sel+1], self.__column_widths[sel] = self.__column_widths[sel], self.__column_widths[sel+1] self.__selected_column += 1 self._place_items() self._update_button_visibility() def on_remove_column(self): if self.__selected_column == -1: return sel = self.__selected_column # remove item from scenes item, legend = self.__columns[sel] self.__log_scene.removeItem(legend) self.__log_scene.removeItem(item) # remove from internal list del self.__columns[sel] del self.__column_widths[sel] self.__selected_column = -1 self._place_items() self._update_button_visibility() def on_edit_style(self): if self.__selected_column == -1: return item = self.__columns[self.__selected_column][0] item.edit_style() def on_add_column(self): # to be overridden by subclasses pass def styles(self): """Return the current style of each item""" return dict([(item.layer().id(), item.qgis_style()) for item, legend in self.__columns if hasattr(item, "qgis_style")])
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) # 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.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) 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.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.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.mActionExportPython.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.zoom = 1 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) 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): 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() alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm_id) if alg is not None: self._addAlgorithm(alg, event.pos()) else: assert False, algorithm_id elif event.mimeData().hasText(): itemId = event.mimeData().text() if itemId in [ param.id() for param in QgsApplication.instance(). processingRegistry().parameterTypes() ]: self.addInputOfType(itemId, event.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) self.algorithmTree.setFilters( QgsProcessingToolboxProxyModel.FilterModeler) 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.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 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) 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.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.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)
class TreeSettingItem(QTreeWidgetItem): comboStyle = '''QComboBox { border: 1px solid gray; border-radius: 3px; padding: 1px 18px 1px 3px; min-width: 6em; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 15px; border-left-width: 1px; border-left-color: darkgray; border-left-style: solid; border-top-right-radius: 3px; border-bottom-right-radius: 3px; } ''' def _addTextEdit(self, editable=True): layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.textEdit = QTextEdit() if not editable: self.textEdit.setReadOnly(True) self.textEdit.setPlainText(self._value) layout.addWidget(self.textEdit) w = QWidget() w.setLayout(layout) self.tree.setItemWidget(self, 1, w) def _addTextBoxWithLink(self, text, func, editable=True): layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.lineEdit = QLineEdit() if not editable: self.lineEdit.setReadOnly(True) self.lineEdit.setText(self._value) layout.addWidget(self.lineEdit) if text: self.linkLabel = QLabel() self.linkLabel.setText("<a href='#'> %s</a>" % text) self.linkLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) layout.addWidget(self.linkLabel) self.linkLabel.linkActivated.connect(func) w = QWidget() w.setLayout(layout) self.tree.setItemWidget(self, 1, w) def __init__(self, parent, tree, setting, namespace, value): QTreeWidgetItem.__init__(self, parent) self.parent = parent self.namespace = namespace self.tree = tree self._value = value self.setting = setting self.name = setting["name"] self.labelText = setting["label"] self.settingType = setting["type"] self.setText(0, self.labelText) if self.settingType == CRS: def edit(): selector = QgsProjectionSelectionDialog() selector.setCrs(value); if selector.exec_(): crs = selector.crs() if crs.upper().startswith("EPSG:"): self.lineEdit.setText(crs) self._addTextBoxWithLink("Edit", edit, False) elif self.settingType == FILES: def edit(): f = QFileDialog.getOpenFileNames(parent.treeWidget(), "Select file", "", "*.*") if f: self.lineEdit.setText(",".join(f)) self._addTextBoxWithLink("Browse", edit, True) elif self.settingType == FILE: def edit(): f = QFileDialog.getOpenFileName(parent.treeWidget(), "Select file", "", "*.*") if f: self.lineEdit.setText(f) self._addTextBoxWithLink("Browse", edit, True) elif self.settingType == FOLDER: def edit(): f = QFileDialog.getExistingDirectory(parent.treeWidget(), "Select folder", "") if f: self.lineEdit.setText(f) self._addTextBoxWithLink("Browse", edit, True) elif self.settingType == BOOL: if value: self.setCheckState(1, Qt.Checked) else: self.setCheckState(1, Qt.Unchecked) elif self.settingType == CHOICE: self.combo = QComboBox() self.combo.setStyleSheet(self.comboStyle) for option in setting["options"]: self.combo.addItem(option) self.tree.setItemWidget(self, 1, self.combo) idx = self.combo.findText(str(value)) self.combo.setCurrentIndex(idx) elif self.settingType == TEXT: self._addTextEdit() elif self.settingType == STRING: self._addTextBoxWithLink(None, None) elif self.settingType == AUTHCFG: def edit(): currentAuthCfg = self.value() dlg = AuthConfigSelectDialog(parent.treeWidget(), authcfg=currentAuthCfg) ret = dlg.exec_() if ret: self.lineEdit.setText(dlg.authcfg) self._addTextBoxWithLink("Select", edit, True) else: self.setFlags(self.flags() | Qt.ItemIsEditable) self.setText(1, unicode(value)) def saveValue(self): value = self.value() setPluginSetting(self.name, value, self.namespace) def value(self): self.setBackgroundColor(0, Qt.white) self.setBackgroundColor(1, Qt.white) try: if self.settingType == BOOL: return self.checkState(1) == Qt.Checked elif self.settingType == NUMBER: v = float(self.text(1)) return v elif self.settingType == CHOICE: return self.combo.currentText() elif self.settingType in [TEXT]: return self.textEdit.toPlainText() elif self.settingType in [CRS, STRING, FILES, FOLDER, AUTHCFG]: return self.lineEdit.text() else: return self.text(1) except: self.setBackgroundColor(0, Qt.yellow) self.setBackgroundColor(1, Qt.yellow) raise WrongValueException() def setValue(self, value): if self.settingType == BOOL: if value: self.setCheckState(1, Qt.Checked) else: self.setCheckState(1, Qt.Unchecked) elif self.settingType == CHOICE: idx = self.combo.findText(str(value)) self.combo.setCurrentIndex(idx) elif self.settingType in [TEXT, CRS, STRING, FILES, FOLDER, AUTHCFG]: self.lineEdit.setText(value) else: self.setText(1, unicode(value)) def resetDefault(self): self.setValue(self.setting["default"])
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) # 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.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) 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 = QTreeWidget(self.scrollAreaWidgetContents_3) 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.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 self.mToolbar.setIconSize(iface.iconSize()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.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.mActionExportPython.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.zoom = 1 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) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): itemId = event.mimeData().text() if itemId in [param.id() for param in QgsApplication.instance().processingRegistry().parameterTypes()]: self.addInputOfType(itemId, event.pos()) else: alg = QgsApplication.processingRegistry().createAlgorithmById(itemId) if alg is not None: self._addAlgorithm(alg, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): 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) def _mimeDataAlgorithm(items): item = items[0] mimeData = None if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.id()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) 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.textChanged) 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.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.fillInputsTree() self.fillTreeUsingProviders() 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 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) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(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.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("Model was correctly exported as image"), 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("Model was correctly exported as PDF"), 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("Model was correctly exported as SVG"), 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('Python files (*.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("Model was correctly exported as python script"), level=Qgis.Success, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) 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)')) 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() 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.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 textChanged(self): text = self.searchBox.text().strip(' ').lower() for item in list(self.disabledProviderItems.values()): item.setHidden(True) self._filterItem(self.algorithmTree.invisibleRootItem(), [t for t in text.split(' ') if t]) if text: self.algorithmTree.expandAll() self.disabledWithMatchingAlgs = [] for provider in QgsApplication.processingRegistry().providers(): if not provider.isActive(): for alg in provider.algorithms(): if text in alg.name(): self.disabledWithMatchingAlgs.append(provider.id()) break else: self.algorithmTree.collapseAll() def _filterItem(self, item, text): if (item.childCount() > 0): show = False for i in range(item.childCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) and item not in list(self.disabledProviderItems.values()) item.setHidden(not show) return show elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)): # hide if every part of text is not contained somewhere in either the item text or item user role item_text = [item.text(0).lower(), item.data(0, ModelerDialog.NAME_ROLE).lower()] if isinstance(item, TreeAlgorithmItem): item_text.append(item.alg.id().lower()) if item.alg.shortDescription(): item_text.append(item.alg.shortDescription().lower()) item_text.extend([t.lower() for t in item.data(0, ModelerDialog.TAG_ROLE)]) hide = bool(text) and not all( any(part in t for t in item_text) for part in text) item.setHidden(hide) return not hide else: item.setHidden(True) return False 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): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = QgsApplication.processingRegistry().createAlgorithmById(item.alg.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 fillTreeUsingProviders(self): self.algorithmTree.clear() self.disabledProviderItems = {} # TODO - replace with proper model for toolbox! # first add qgis/native providers, since they create top level groups for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): self.addAlgorithmsFromProvider(provider, self.algorithmTree.invisibleRootItem()) else: continue self.algorithmTree.sortItems(0, Qt.AscendingOrder) for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): # already added continue else: providerItem = TreeProviderItem(provider, self.algorithmTree, self) # insert non-native providers at end of tree, alphabetically for i in range(self.algorithmTree.invisibleRootItem().childCount()): child = self.algorithmTree.invisibleRootItem().child(i) if isinstance(child, TreeProviderItem): if child.text(0) > providerItem.text(0): break self.algorithmTree.insertTopLevelItem(i + 1, providerItem) if not provider.isActive(): providerItem.setHidden(True) self.disabledProviderItems[provider.id()] = providerItem def addAlgorithmsFromProvider(self, provider, parent): groups = {} count = 0 algs = provider.algorithms() active = provider.isActive() # Add algorithms for alg in algs: if alg.flags() & QgsProcessingAlgorithm.FlagHideFromModeler: continue groupItem = None if alg.group() in groups: groupItem = groups[alg.group()] else: # check if group already exists for i in range(parent.childCount()): if parent.child(i).text(0) == alg.group(): groupItem = parent.child(i) groups[alg.group()] = groupItem break if not groupItem: groupItem = TreeGroupItem(alg.group()) if not active: groupItem.setInactive() if provider.id() in ('qgis', 'native', '3d'): groupItem.setIcon(0, provider.icon()) groups[alg.group()] = groupItem algItem = TreeAlgorithmItem(alg) if not active: algItem.setForeground(0, Qt.darkGray) groupItem.addChild(algItem) count += 1 text = provider.name() if not provider.id() in ('qgis', 'native', '3d'): if not active: def activateProvider(): self.activateProvider(provider.id()) label = QLabel(text + " <a href='%s'>Activate</a>") label.setStyleSheet("QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) self.algorithmTree.setItemWidget(parent, 0, label) else: parent.setText(0, text) for group, groupItem in sorted(groups.items(), key=operator.itemgetter(1)): parent.addChild(groupItem) if not provider.id() in ('qgis', 'native', '3d'): parent.setHidden(parent.childCount() == 0)
class QdrawSettings(QWidget): """Window used to change settings (transparency/color)""" settingsChanged = pyqtSignal() def __init__(self): QWidget.__init__(self) self.setWindowTitle(self.tr('Qdraw - Settings')) self.setFixedSize(320, 100) self.center() # default color self.color = QColor(60, 151, 255, 255) self.sld_opacity = QSlider(Qt.Horizontal, self) self.sld_opacity.setRange(0, 255) self.sld_opacity.setValue(255) self.sld_opacity.tracking = True self.sld_opacity.valueChanged.connect(self.handler_opacitySliderValue) self.lbl_opacity = QLabel(self.tr('Opacity') + ': 100%', self) self.dlg_color = QColorDialog(self) btn_chColor = QPushButton(self.tr('Change the drawing color'), self) btn_chColor.clicked.connect(self.handler_chColor) vbox = QVBoxLayout() vbox.addWidget(self.lbl_opacity) vbox.addWidget(self.sld_opacity) vbox.addWidget(btn_chColor) self.setLayout(vbox) def tr(self, message): return QCoreApplication.translate('Qdraw', message) def handler_opacitySliderValue(self, val): self.color.setAlpha(val) self.lbl_opacity.setText( self.tr('Opacity')+': '+str(int((float(val) / 255) * 100))+'%') self.settingsChanged.emit() def handler_chColor(self): color = self.dlg_color.getColor(self.color) if color.isValid(): color.setAlpha(self.color.alpha()) self.color = color self.settingsChanged.emit() self.close() def getColor(self): return self.color def center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def closeEvent(self, e): self.clear() e.accept() def clear(self): return
class DistanceInputPanel(NumberInputPanel): """ Distance input panel for use outside the modeler - this input panel contains a label showing the distance unit. """ def __init__(self, param): super().__init__(param) self.label = QLabel('') self.units_combo = QComboBox() self.base_units = QgsUnitTypes.DistanceUnknownUnit for u in (QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceKilometers, QgsUnitTypes.DistanceFeet, QgsUnitTypes.DistanceMiles, QgsUnitTypes.DistanceYards): self.units_combo.addItem(QgsUnitTypes.toString(u), u) label_margin = self.fontMetrics().width('X') self.layout().insertSpacing(1, label_margin / 2) self.layout().insertWidget(2, self.label) self.layout().insertWidget(3, self.units_combo) self.layout().insertSpacing(4, label_margin / 2) self.warning_label = QLabel() icon = QgsApplication.getThemeIcon('mIconWarning.svg') size = max(24, self.spnValue.height() * 0.5) self.warning_label.setPixmap(icon.pixmap(icon.actualSize(QSize(size, size)))) self.warning_label.setToolTip(self.tr('Distance is in geographic degrees. Consider reprojecting to a projected local coordinate system for accurate results.')) self.layout().insertWidget(4, self.warning_label) self.layout().insertSpacing(5, label_margin) self.setUnits(QgsUnitTypes.DistanceUnknownUnit) def setUnits(self, units): self.label.setText(QgsUnitTypes.toString(units)) if QgsUnitTypes.unitType(units) != QgsUnitTypes.Standard: self.units_combo.hide() self.label.show() else: self.units_combo.setCurrentIndex(self.units_combo.findData(units)) self.units_combo.show() self.label.hide() self.warning_label.setVisible(units == QgsUnitTypes.DistanceDegrees) self.base_units = units def setUnitParameterValue(self, value): units = QgsUnitTypes.DistanceUnknownUnit layer = self.getLayerFromValue(value) if isinstance(layer, QgsMapLayer): units = layer.crs().mapUnits() elif isinstance(value, QgsCoordinateReferenceSystem): units = value.mapUnits() elif isinstance(value, str): crs = QgsCoordinateReferenceSystem(value) if crs.isValid(): units = crs.mapUnits() self.setUnits(units) def getValue(self): val = super().getValue() if isinstance(val, float) and self.units_combo.isVisible(): display_unit = self.units_combo.currentData() return val * QgsUnitTypes.fromUnitToUnitFactor(display_unit, self.base_units) return val def setValue(self, value): try: self.spnValue.setValue(float(value)) except: return
class CreateElectorateDialog(QDialog): """ Custom dialog for entering properties for a new electorate """ def __init__(self, registry: LinzElectoralDistrictRegistry, context: LinzRedistrictingContext, parent=None): super().__init__(parent) self.existing_names = list(registry.district_titles().keys()) self.existing_codes = [ f['code'] for f in registry.source_layer.getFeatures() ] self.setWindowTitle( self.tr('Create New {} Electorate').format( context.get_name_for_current_task())) dialog_layout = QVBoxLayout() label = QLabel( self.tr('Enter name for new {} electorate:').format( context.get_name_for_current_task())) dialog_layout.addWidget(label) self.name_line_edit = QLineEdit() self.name_line_edit.setPlaceholderText('Electorate name') dialog_layout.addWidget(self.name_line_edit) label = QLabel(self.tr('Enter code for new electorate:')) dialog_layout.addWidget(label) self.code_line_edit = QLineEdit() self.code_line_edit.setPlaceholderText('Electorate code') dialog_layout.addWidget(self.code_line_edit) self.feedback_label = QLabel() dialog_layout.addWidget(self.feedback_label) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dialog_layout.addWidget(self.button_box) self.button_box.rejected.connect(self.reject) self.button_box.accepted.connect(self.accept) self.name_line_edit.textChanged.connect(self.name_changed) self.code_line_edit.textChanged.connect(self.code_changed) self.setLayout(dialog_layout) self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) def name_changed(self): """ Triggered on name change """ self.__update_feedback_label() def code_changed(self): """ Triggered on code change """ self.__update_feedback_label() def __update_feedback_label(self): """ Updates the dialog feedback label """ name = self.name_line_edit.text().strip() code = self.code_line_edit.text().strip() self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) if name in self.existing_names: self.feedback_label.setText( self.tr('An electorate with this name already exists!')) elif not name: self.feedback_label.setText( self.tr('An electorate name must be entered!')) elif code in self.existing_codes: self.feedback_label.setText( self.tr('An electorate with this code already exists!')) elif not code: self.feedback_label.setText( self.tr('An electorate code must be entered!')) else: self.feedback_label.setText('') self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) def name(self) -> str: """ Returns the current name entered in the dialog """ return self.name_line_edit.text() def code(self) -> str: """ Returns the current code entered in the dialog """ return self.code_line_edit.text()
def treeItemChanged(self, current, previous): qgsgeom1 = None qgsgeom2 = None crs = "EPSG:4326" if not isinstance(current, FeatureItem): self.attributesTable.clear() self.attributesTable.setRowCount(0) return color = {"MODIFIED": QColor(255, 170, 0), "ADDED":Qt.green, "REMOVED":Qt.red , "NO_CHANGE":Qt.white} path = current.layername + "/" + current.featureid featurediff = self.changes[path].featurediff() self.attributesTable.clear() self.attributesTable.verticalHeader().show() self.attributesTable.horizontalHeader().show() self.attributesTable.setRowCount(len(featurediff)) self.attributesTable.setVerticalHeaderLabels([a["attributename"] for a in featurediff]) self.attributesTable.setHorizontalHeaderLabels(["Old value", "New value", "Change type"]) for i, attrib in enumerate(featurediff): try: if attrib["changetype"] == "MODIFIED": oldvalue = attrib["oldvalue"] newvalue = attrib["newvalue"] elif attrib["changetype"] == "ADDED": newvalue = attrib["newvalue"] oldvalue = "" elif attrib["changetype"] == "REMOVED": oldvalue = attrib["oldvalue"] newvalue = "" else: oldvalue = newvalue = attrib["oldvalue"] except: oldvalue = newvalue = "" self.attributesTable.setItem(i, 0, DiffItem(oldvalue)) self.attributesTable.setItem(i, 1, DiffItem(newvalue)) try: self.attributesTable.setItem(i, 2, QTableWidgetItem("")) if qgsgeom1 is None or qgsgeom2 is None: if "crs" in attrib: crs = attrib["crs"] qgsgeom1 = QgsGeometry.fromWkt(oldvalue) qgsgeom2 = QgsGeometry.fromWkt(newvalue) if qgsgeom1 is not None and qgsgeom2 is not None: widget = QWidget() btn = QPushButton() btn.setText("View detail") btn.clicked.connect(lambda: self.viewGeometryChanges(qgsgeom1, qgsgeom2, crs)) label = QLabel() label.setText(attrib["changetype"]) layout = QHBoxLayout(widget) layout.addWidget(label); layout.addWidget(btn); layout.setContentsMargins(0, 0, 0, 0) widget.setLayout(layout) self.attributesTable.setCellWidget(i, 2, widget) else: self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"])) else: self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"])) except: self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"])) for col in range(3): self.attributesTable.item(i, col).setBackgroundColor(color[attrib["changetype"]]); self.attributesTable.resizeColumnsToContents() self.attributesTable.horizontalHeader().setResizeMode(QHeaderView.Stretch)
def construct_form_param_user(self, row, pos): widget = None for field in row[pos]['fields']: if field['label']: lbl = QLabel() lbl.setObjectName('lbl' + field['widgetname']) lbl.setText(field['label']) lbl.setMinimumSize(160, 0) lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) lbl.setToolTip(field['tooltip']) chk = QCheckBox() chk.setObjectName('chk_' + field['widgetname']) if field['checked'] in ('true', 'True', 'TRUE', True): chk.setChecked(True) elif field['checked'] in ('false', 'False', 'FALSE', False): chk.setChecked(False) chk.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) if field['widgettype'] == 'text' or field[ 'widgettype'] == 'linetext' or field[ 'widgettype'] == 'typeahead': widget = QLineEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_user, chk, widget, field)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) if field['widgettype'] == 'typeahead': completer = QCompleter() if 'dv_querytext' in field or 'dv_querytext_filterc' in field: widget.setProperty('typeahead', True) model = QStringListModel() widget.textChanged.connect( partial(self.populate_typeahead, completer, model, field, self.dlg_config, widget)) elif field['widgettype'] == 'textarea': widget = QTextEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_user, chk, widget, field)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'combo': widget = QComboBox() self.populate_combo(widget, field) widget.currentIndexChanged.connect( partial(self.get_values_changed_param_user, chk, widget, field)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'check': widget = chk widget.stateChanged.connect( partial(self.get_values_changed_param_user, chk, chk, field)) widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) elif field['widgettype'] == 'datetime': widget = QgsDateTimeEdit() widget.setAllowNull(True) widget.setCalendarPopup(True) widget.setDisplayFormat('dd/MM/yyyy') if field['value']: field['value'] = field['value'].replace('/', '-') date = QDate.fromString(field['value'], 'yyyy-MM-dd') if date: widget.setDate(date) else: widget.clear() widget.dateChanged.connect( partial(self.get_values_changed_param_user, chk, widget, field)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'spinbox': widget = QDoubleSpinBox() if 'value' in field and field['value'] is not None: value = float(str(field['value'])) widget.setValue(value) widget.valueChanged.connect( partial(self.get_values_changed_param_user, chk, widget, field)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) else: pass widget.setObjectName(field['widgetname']) # Set signals chk.stateChanged.connect( partial(self.get_values_checked_param_user, chk, widget, field)) if field['layoutname'] == 'lyt_basic': self.order_widgets(field, self.basic_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_om': self.order_widgets(field, self.om_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_inventory': self.order_widgets(field, self.inventory_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_mapzones': self.order_widgets(field, self.mapzones_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_edit': self.order_widgets(field, self.cad_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_epa': self.order_widgets(field, self.epa_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_masterplan': self.order_widgets(field, self.masterplan_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_other': self.order_widgets(field, self.other_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_node_vdef': self.order_widgets(field, self.node_type_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_arc_vdef': self.order_widgets(field, self.cat_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_utils_vdef': self.order_widgets(field, self.utils_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_connec_vdef': self.order_widgets(field, self.connec_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_gully_vdef': self.order_widgets(field, self.gully_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_fluid_type': self.order_widgets(field, self.fluid_type_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_location_type': self.order_widgets(field, self.location_type_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_category_type': self.order_widgets(field, self.category_type_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_function_type': self.order_widgets(field, self.function_type_form, lbl, chk, widget) elif field['layoutname'] == 'lyt_addfields': self.order_widgets(field, self.addfields_form, lbl, chk, widget)
class QmsSearchResultItemWidget(QWidget): def __init__(self, geoservice, image_ba, parent=None, extent_renderer=None): QWidget.__init__(self, parent) self.extent_renderer = extent_renderer self.layout = QHBoxLayout(self) self.layout.setContentsMargins(5, 10, 5, 10) self.setLayout(self.layout) self.service_icon = QLabel(self) self.service_icon.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.service_icon.resize(24, 24) qimg = QImage.fromData(image_ba) pixmap = QPixmap.fromImage(qimg) self.service_icon.setPixmap(pixmap) self.layout.addWidget(self.service_icon) self.service_desc_layout = QGridLayout(self) self.service_desc_layout.setSpacing(0) self.layout.addLayout(self.service_desc_layout) self.service_name = QLabel(self) self.service_name.setTextFormat(Qt.RichText) self.service_name.setWordWrap(True) self.service_name.setText(u" <strong> {} </strong>".format( geoservice.get('name', u""))) self.service_desc_layout.addWidget(self.service_name, 0, 0, 1, 3) self.service_type = QLabel(self) self.service_type.setTextFormat(Qt.RichText) self.service_type.setWordWrap(True) self.service_type.setText(geoservice.get('type', u"").upper() + " ") self.service_desc_layout.addWidget(self.service_type, 1, 0) self.service_deteils = QLabel(self) self.service_deteils.setTextFormat(Qt.RichText) self.service_deteils.setWordWrap(True) self.service_deteils.setOpenExternalLinks(True) self.service_deteils.setText(u"<a href=\"{0}\">{1}</a>, ".format( Client().geoservice_info_url(geoservice.get('id', u"")), self.tr('details'))) self.service_desc_layout.addWidget(self.service_deteils, 1, 1) self.service_report = QLabel(self) self.service_report.setTextFormat(Qt.RichText) self.service_report.setWordWrap(True) self.service_report.setOpenExternalLinks(True) self.service_report.setText(u"<a href=\"{0}\">{1}</a><div/>".format( Client().geoservice_report_url(geoservice.get('id', u"")), self.tr('report a problem'))) self.service_desc_layout.addWidget(self.service_report, 1, 2) self.service_desc_layout.setColumnStretch(2, 1) self.status_label = QLabel(self) self.status_label.setTextFormat(Qt.RichText) self.status_label.setText(u'\u2022') status = geoservice.get('cumulative_status', u'') if status == 'works': self.status_label.setStyleSheet("color: green; font-size: 30px") if status == 'failed': self.status_label.setStyleSheet("color: red; font-size: 30px") if status == 'problematic': self.status_label.setStyleSheet("color: yellow; font-size: 30px") self.layout.addWidget(self.status_label) self.addButton = QToolButton() self.addButton.setText(self.tr("Add")) self.addButton.clicked.connect(self.addToMap) self.layout.addWidget(self.addButton) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) self.geoservice = geoservice self.image_ba = image_ba def addToMap(self): try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) client = Client() client.set_proxy(*QGISSettings.get_qgis_proxy()) geoservice_info = client.get_geoservice_info(self.geoservice) ds = DataSourceSerializer.read_from_json(geoservice_info) add_layer_to_map(ds) CachedServices().add_service(self.geoservice, self.image_ba) except Exception as ex: plPrint(unicode(ex)) pass finally: QApplication.restoreOverrideCursor() def mouseDoubleClickEvent(self, event): self.addToMap() def enterEvent(self, event): extent = self.geoservice.get('extent', None) if self.extent_renderer and extent: if ';' in extent: extent = extent.split(';')[1] geom = QgsGeometry.fromWkt(extent) self.extent_renderer.show_feature(geom) def leaveEvent(self, event): if self.extent_renderer: self.extent_renderer.clear_feature()
class Ui_DistroMap(object): def setupUi(self, DistroMap): DistroMap.setObjectName("DistroMap") DistroMap.resize(439, 657) self.gridLayout_3 = QGridLayout(DistroMap) self.gridLayout_3.setObjectName("gridLayout_3") self.scrollArea = QScrollArea(DistroMap) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 419, 573)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.gridLayout_6 = QGridLayout(self.scrollAreaWidgetContents) self.gridLayout_6.setObjectName("gridLayout_6") self.verticalLayout = QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.label_5 = QLabel(self.scrollAreaWidgetContents) self.label_5.setObjectName("label_5") self.verticalLayout.addWidget(self.label_5) self.comboLocalities = QComboBox(self.scrollAreaWidgetContents) self.comboLocalities.setObjectName("comboLocalities") self.verticalLayout.addWidget(self.comboLocalities) self.gridLayout_6.addLayout(self.verticalLayout, 1, 0, 1, 1) self.horizontalLayout_6 = QHBoxLayout() self.horizontalLayout_6.setObjectName("horizontalLayout_6") self.verticalLayout_13 = QVBoxLayout() self.verticalLayout_13.setObjectName("verticalLayout_13") self.label_19 = QLabel(self.scrollAreaWidgetContents) self.label_19.setObjectName("label_19") self.verticalLayout_13.addWidget(self.label_19) self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.spnOutWidth = QSpinBox(self.scrollAreaWidgetContents) self.spnOutWidth.setMaximum(999999) self.spnOutWidth.setProperty("value", 325) self.spnOutWidth.setObjectName("spnOutWidth") self.horizontalLayout_3.addWidget(self.spnOutWidth) self.label_20 = QLabel(self.scrollAreaWidgetContents) self.label_20.setAlignment(Qt.AlignCenter) self.label_20.setObjectName("label_20") self.horizontalLayout_3.addWidget(self.label_20) self.spnOutHeight = QSpinBox(self.scrollAreaWidgetContents) self.spnOutHeight.setMaximum(999999) self.spnOutHeight.setProperty("value", 299) self.spnOutHeight.setObjectName("spnOutHeight") self.horizontalLayout_3.addWidget(self.spnOutHeight) self.verticalLayout_13.addLayout(self.horizontalLayout_3) self.horizontalLayout_6.addLayout(self.verticalLayout_13) self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.verticalLayout_14 = QVBoxLayout() self.verticalLayout_14.setObjectName("verticalLayout_14") self.label_21 = QLabel(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_21.sizePolicy().hasHeightForWidth()) self.label_21.setSizePolicy(sizePolicy) self.label_21.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.label_21.setObjectName("label_21") self.verticalLayout_14.addWidget(self.label_21) self.horizontalLayout_4 = QHBoxLayout() self.horizontalLayout_4.setObjectName("horizontalLayout_4") spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem) self.btnColour = QPushButton(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btnColour.sizePolicy().hasHeightForWidth()) self.btnColour.setSizePolicy(sizePolicy) self.btnColour.setObjectName("btnColour") self.horizontalLayout_4.addWidget(self.btnColour) self.verticalLayout_14.addLayout(self.horizontalLayout_4) self.horizontalLayout_5.addLayout(self.verticalLayout_14) self.frmColour = QFrame(self.scrollAreaWidgetContents) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frmColour.sizePolicy().hasHeightForWidth()) self.frmColour.setSizePolicy(sizePolicy) self.frmColour.setMinimumSize(QSize(45, 45)) self.frmColour.setSizeIncrement(QSize(1, 1)) self.frmColour.setBaseSize(QSize(0, 0)) self.frmColour.setFrameShape(QFrame.StyledPanel) self.frmColour.setFrameShadow(QFrame.Raised) self.frmColour.setObjectName("frmColour") self.horizontalLayout_5.addWidget(self.frmColour) self.horizontalLayout_6.addLayout(self.horizontalLayout_5) self.gridLayout_6.addLayout(self.horizontalLayout_6, 4, 0, 1, 1) self.verticalLayout_2 = QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.label_6 = QLabel(self.scrollAreaWidgetContents) self.label_6.setObjectName("label_6") self.verticalLayout_2.addWidget(self.label_6) self.comboTaxonField = QComboBox(self.scrollAreaWidgetContents) self.comboTaxonField.setObjectName("comboTaxonField") self.verticalLayout_2.addWidget(self.comboTaxonField) self.gridLayout_6.addLayout(self.verticalLayout_2, 2, 0, 1, 1) self.verticalLayout_5 = QVBoxLayout() self.verticalLayout_5.setObjectName("verticalLayout_5") self.label_9 = QLabel(self.scrollAreaWidgetContents) self.label_9.setObjectName("label_9") self.verticalLayout_5.addWidget(self.label_9) self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.leOutDir = QLineEdit(self.scrollAreaWidgetContents) self.leOutDir.setPlaceholderText("") self.leOutDir.setObjectName("leOutDir") self.horizontalLayout.addWidget(self.leOutDir) self.btnBrowse = QPushButton(self.scrollAreaWidgetContents) self.btnBrowse.setObjectName("btnBrowse") self.horizontalLayout.addWidget(self.btnBrowse) self.verticalLayout_5.addLayout(self.horizontalLayout) self.gridLayout_6.addLayout(self.verticalLayout_5, 5, 0, 1, 1) self.verticalLayout_6 = QVBoxLayout() self.verticalLayout_6.setObjectName("verticalLayout_6") self.label = QLabel(self.scrollAreaWidgetContents) self.label.setObjectName("label") self.verticalLayout_6.addWidget(self.label) self.gridLayout = QGridLayout() self.gridLayout.setObjectName("gridLayout") self.label_2 = QLabel(self.scrollAreaWidgetContents) self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1) self.comboSecondary = QComboBox(self.scrollAreaWidgetContents) self.comboSecondary.setObjectName("comboSecondary") self.gridLayout.addWidget(self.comboSecondary, 1, 1, 1, 1) self.comboSurface = QComboBox(self.scrollAreaWidgetContents) self.comboSurface.setObjectName("comboSurface") self.gridLayout.addWidget(self.comboSurface, 2, 1, 1, 1) self.label_3 = QLabel(self.scrollAreaWidgetContents) self.label_3.setObjectName("label_3") self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1) self.label_4 = QLabel(self.scrollAreaWidgetContents) self.label_4.setObjectName("label_4") self.gridLayout.addWidget(self.label_4, 2, 0, 1, 1) self.comboBase = QComboBox(self.scrollAreaWidgetContents) self.comboBase.setObjectName("comboBase") self.gridLayout.addWidget(self.comboBase, 0, 1, 1, 1) self.gridLayout.setColumnStretch(0, 1) self.gridLayout.setColumnStretch(1, 3) self.verticalLayout_6.addLayout(self.gridLayout) self.gridLayout_6.addLayout(self.verticalLayout_6, 0, 0, 1, 1) self.verticalLayout_3 = QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.label_7 = QLabel(self.scrollAreaWidgetContents) self.label_7.setObjectName("label_7") self.verticalLayout_3.addWidget(self.label_7) self.comboGrid = QComboBox(self.scrollAreaWidgetContents) self.comboGrid.setObjectName("comboGrid") self.verticalLayout_3.addWidget(self.comboGrid) self.verticalLayout_4 = QVBoxLayout() self.verticalLayout_4.setObjectName("verticalLayout_4") self.label_8 = QLabel(self.scrollAreaWidgetContents) self.label_8.setObjectName("label_8") self.verticalLayout_4.addWidget(self.label_8) self.gridLayout_2 = QGridLayout() self.gridLayout_2.setObjectName("gridLayout_2") self.leMaxY = QLineEdit(self.scrollAreaWidgetContents) self.leMaxY.setObjectName("leMaxY") self.gridLayout_2.addWidget(self.leMaxY, 0, 1, 1, 1) self.leMinX = QLineEdit(self.scrollAreaWidgetContents) self.leMinX.setObjectName("leMinX") self.gridLayout_2.addWidget(self.leMinX, 1, 0, 1, 1) self.leMaxX = QLineEdit(self.scrollAreaWidgetContents) self.leMaxX.setObjectName("leMaxX") self.gridLayout_2.addWidget(self.leMaxX, 1, 2, 1, 1) self.leMinY = QLineEdit(self.scrollAreaWidgetContents) self.leMinY.setObjectName("leMinY") self.gridLayout_2.addWidget(self.leMinY, 2, 1, 1, 1) self.btnExtent = QPushButton(self.scrollAreaWidgetContents) self.btnExtent.setObjectName("btnExtent") self.gridLayout_2.addWidget(self.btnExtent, 1, 1, 1, 1) self.verticalLayout_4.addLayout(self.gridLayout_2) self.verticalLayout_3.addLayout(self.verticalLayout_4) self.gridLayout_6.addLayout(self.verticalLayout_3, 3, 0, 1, 1) self.scrollArea.setWidget(self.scrollAreaWidgetContents) self.gridLayout_3.addWidget(self.scrollArea, 0, 0, 1, 1) self.buttonBox = QDialogButtonBox(DistroMap) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.gridLayout_3.addWidget(self.buttonBox, 2, 0, 1, 1) self.progressBar = QProgressBar(DistroMap) self.progressBar.setProperty("value", 0) self.progressBar.setObjectName("progressBar") self.gridLayout_3.addWidget(self.progressBar, 1, 0, 1, 1) self.retranslateUi(DistroMap) self.buttonBox.rejected.connect(DistroMap.reject) QMetaObject.connectSlotsByName(DistroMap) def retranslateUi(self, DistroMap): DistroMap.setWindowTitle(QApplication.translate("DistroMap", "Distribution Map Generator", None)) self.label_5.setText(QApplication.translate("DistroMap", "Point localities layer", None)) self.label_19.setText(QApplication.translate("DistroMap", "Output resolution", None)) self.label_20.setText(QApplication.translate("DistroMap", "x", None)) self.label_21.setText(QApplication.translate("DistroMap", "Map background", None)) self.btnColour.setText(QApplication.translate("DistroMap", "Change", None)) self.label_6.setText(QApplication.translate("DistroMap", "Taxon identifier field", None)) self.label_9.setText(QApplication.translate("DistroMap", "Output directory", None)) self.btnBrowse.setText(QApplication.translate("DistroMap", "Browse...", None)) self.label.setText(QApplication.translate("DistroMap", "Background layers:", None)) self.label_2.setText(QApplication.translate("DistroMap", "Base", None)) self.label_3.setText(QApplication.translate("DistroMap", "Secondary", None)) self.label_4.setText(QApplication.translate("DistroMap", "Surface", None)) self.label_7.setText(QApplication.translate("DistroMap", "Grid layer", None)) self.label_8.setText(QApplication.translate("DistroMap", "Output extent:", None)) self.leMaxY.setText(QApplication.translate("DistroMap", "-21.00", None)) self.leMinX.setText(QApplication.translate("DistroMap", "14.75", None)) self.leMaxX.setText(QApplication.translate("DistroMap", "34.00", None)) self.leMinY.setText(QApplication.translate("DistroMap", "-36.00", None)) self.btnExtent.setText(QApplication.translate("DistroMap", "Use current", None))