def main(): """Main function to run the example.""" app = QApplication([]) default_value_parameter = DefaultValueParameter() default_value_parameter.name = 'Value parameter' default_value_parameter.help_text = 'Help text' default_value_parameter.description = 'Description' default_value_parameter.labels = [ 'Setting', 'Do not report', 'Custom'] default_value_parameter.options = [0, 1, None] parameters = [ default_value_parameter ] extra_parameters = [ (DefaultValueParameter, DefaultValueParameterWidget) ] parameter_container = ParameterContainer( parameters, extra_parameters=extra_parameters) parameter_container.setup_ui() widget = QWidget() layout = QGridLayout() layout.addWidget(parameter_container) widget.setLayout(layout) widget.setGeometry(0, 0, 500, 500) widget.show() sys.exit(app.exec_())
def __init__(self, options, multiple, columns=2, parent=None): super(CheckboxesPanel, self).__init__(parent) self._options = [] for i, option in enumerate(options): if isinstance(option, str): self._options.append((i, option)) else: self.options.append(option) self._multiple = multiple self._buttons = [] rows = len(options) / columns self._buttonGroup = QButtonGroup() self._buttonGroup.setExclusive(not multiple) layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setMargin(0) for i, (v, t) in enumerate(self._options): if multiple: button = QCheckBox(t) else: button = QRadioButton(t) self._buttons.append((v, button)) self._buttonGroup.addButton(button, i) layout.addWidget(button, i % rows, i / rows) layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, columns) self.setLayout(layout) if multiple: self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.showPopupMenu)
def getLayout(parent, widgets): lyt = QGridLayout( parent ) for item in widgets: if 'spam' in item: sRow, sCol = item['spam']['row'], item['spam']['col'] lyt.addWidget( item['widget'], item['row'], item['col'], sRow, sCol, Qt.AlignLeft ) else: lyt.addWidget( item['widget'], item['row'], item['col'], Qt.AlignLeft ) return lyt
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 main(): """Main function to run the example.""" layer = load_test_vector_layer( 'aggregation', 'district_osm_jakarta.geojson', clone=True) app = QApplication([]) field_mapping = FieldMappingTab(age_ratio_group, PARENT, IFACE) field_mapping.set_layer(layer) widget = QWidget() layout = QGridLayout() layout.addWidget(field_mapping) widget.setLayout(layout) widget.show() sys.exit(app.exec_())
class Ui_PostNAS_SearchDialogBase(object): def setupUi(self, PostNAS_SearchDialogBase): PostNAS_SearchDialogBase.setObjectName(_fromUtf8("PostNAS_SearchDialogBase")) PostNAS_SearchDialogBase.resize(501, 337) self.gridLayout = QGridLayout(PostNAS_SearchDialogBase) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.treeWidget = QTreeWidget(PostNAS_SearchDialogBase) self.treeWidget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeWidget.setHeaderHidden(True) self.treeWidget.setObjectName(_fromUtf8("treeWidget")) self.treeWidget.headerItem().setText(0, _fromUtf8("1")) self.gridLayout.addWidget(self.treeWidget, 1, 0, 1, 3) self.lineEdit = QLineEdit(PostNAS_SearchDialogBase) self.lineEdit.setObjectName(_fromUtf8("lineEdit")) self.gridLayout.addWidget(self.lineEdit, 0, 0, 1, 3) self.showButton = QToolButton(PostNAS_SearchDialogBase) self.showButton.setEnabled(False) icon = QtGui.QIcon() icon.addPixmap(QPixmap(_fromUtf8(":/plugins/PostNAS_Search/search_16x16.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.showButton.setIcon(icon) self.showButton.setObjectName(_fromUtf8("showButton")) self.gridLayout.addWidget(self.showButton, 2, 2, 1, 1) self.resetButton = QToolButton(PostNAS_SearchDialogBase) self.resetButton.setEnabled(False) icon1 = QIcon() icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/plugins/PostNAS_Search/marker-delete.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.resetButton.setIcon(icon1) self.resetButton.setObjectName(_fromUtf8("resetButton")) self.gridLayout.addWidget(self.resetButton, 2, 1, 1, 1) self.retranslateUi(PostNAS_SearchDialogBase) QtCore.QMetaObject.connectSlotsByName(PostNAS_SearchDialogBase) def retranslateUi(self, PostNAS_SearchDialogBase): PostNAS_SearchDialogBase.setWindowTitle(_translate("PostNAS_SearchDialogBase", "KU_Search", None)) self.showButton.setToolTip(_translate("PostNAS_SearchDialogBase", "Auswahl anzeigen", None)) self.showButton.setText(_translate("PostNAS_SearchDialogBase", "Anzeigen", None)) self.resetButton.setToolTip(_translate("PostNAS_SearchDialogBase", "Ergebnis löschen", None)) self.resetButton.setText(_translate("PostNAS_SearchDialogBase", "Reset", None))
def main(): """Main function to run the example.""" def print_values(the_profile_widget): data = the_profile_widget.data from pprint import pprint pprint(data) def clear_widget(the_profile_widget): the_profile_widget.clear() def restore_data(the_profile_widget): the_profile_widget.clear() the_profile_widget.data = generate_default_profile() from safe.test.utilities import get_qgis_app QGIS_APP, CANVAS, IFACE, PARENT = get_qgis_app(qsetting=INASAFE_TEST) default_profile = generate_default_profile() profile_widget = ProfileWidget(data=default_profile) get_result_button = QPushButton('Get result...') get_result_button.clicked.connect( partial(print_values, profile_widget)) clear_button = QPushButton('Clear widget...') clear_button.clicked.connect( partial(clear_widget, profile_widget)) restore_button = QPushButton('Restore data...') restore_button.clicked.connect( partial(restore_data, profile_widget)) widget = QWidget() layout = QGridLayout() layout.addWidget(profile_widget) layout.addWidget(restore_button) layout.addWidget(get_result_button) layout.addWidget(clear_button) widget.setLayout(layout) widget.setFixedHeight(600) widget.setFixedWidth(800) widget.show() sys.exit(QGIS_APP.exec_())
class AdvancedSearch(EntityEditorDialog): def __init__(self, entity, parent): EntityEditorDialog.__init__(self, entity, parent=parent) self.parent = parent def _init_gui(self): # Setup base elements self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName('glMain') self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1) QApplication.processEvents() column_widget_area = self._setup_columns_content_area() self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1) QApplication.processEvents() # 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) # if self.has_mandatory: # self.required_fields_lbl = QLabel(self) # msg = self.tr( # 'Please fill out all required (*) fields.' # ) # msg = self._highlight_asterisk(msg) # self.required_fields_lbl.setText(msg) # self.gridLayout.addWidget( # self.required_fields_lbl, next_row, 0, 1, 2 # ) # # Bump up row reference # next_row += 1 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.Cancel) self.search.clicked.connect(self.on_search) # # # # edit model, collect model # # adding new record for child # # # Saving in parent editor # if not isinstance(self._parent._parent, EntityEditorDialog): # # adding a new record # if self.edit_model is None: # # saving when digitizing. # if self.collect_model: # self.buttonBox.accepted.connect(self.on_model_added) # # saving parent editor # else: # self.buttonBox.accepted.connect(self.save_parent_editor) # self.save_new_button.clicked.connect(self.save_and_new) # # updating existing record # else: # if not self.collect_model: # # updating existing record of the parent editor # self.buttonBox.accepted.connect(self.save_parent_editor) # else: # self.buttonBox.accepted.connect(self.on_model_added) # # Saving in child editor # else: # # save and new record # if self.edit_model is None: # self.buttonBox.accepted.connect(self.on_child_saved) # self.save_new_button.clicked.connect( # lambda: self.on_child_saved(True) # ) # # else: # # When updating an existing child editor save to the db # self.buttonBox.accepted.connect( # self.on_child_saved # ) # #self.buttonBox.accepted.connect(self.submit) # self.buttonBox.rejected.connect(self.cancel) def on_search(self): 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 # self.search_db(search_data) result = self.search_db_raw(search_data) self.parent._tableModel.removeRows(0, self.parent._tableModel.rowCount()) if result is not None: found = QApplication.translate('AdvancedSearch', 'records found') new_title = '{} - {} {}'.format(self.title, result.rowcount, found) if result.rowcount > 3000: title = QApplication.translate('AdvancedSearch', 'Advanced Search') message = QApplication.translate( 'AdvancedSearch', 'The search result returned {0} records, which is above the ' 'search result limit. <br>Would you like to see the first 3000 ' 'records?'.format("{:,}".format(result.rowcount))) res, chk_result = simple_dialog(self, title, message) if res: self.setWindowTitle(new_title) self.parent._initializeData(result) else: return else: self.setWindowTitle(new_title) self.parent._initializeData(result) def search_db(self, search_data): ent_model_obj = self.ent_model() # query = ent_model_obj.queryObject() for attr, value in search_data.items(): ent_model_obj.queryObject().filter( getattr(self.ent_model(), attr) == value) # now we can run the query # print(ent_model_obj.queryObject(), vars(ent_model_obj.queryObject())) # print(str(ent_model_obj.queryObject())) results = ent_model_obj.queryObject().all() def search_db_raw(self, search_data): sql = "SELECT * FROM {} WHERE ".format(self._entity.name) # query = ent_model_obj.queryObject() param = [] if len(search_data) == 0: return None for attr, value in search_data.items(): if isinstance(value, (int, float)): param.append('{} = {}'.format(str(attr), str(value))) if isinstance(value, str): param.append("{} = '{}'".format(str(attr), str(value))) final_sql = '{} {}'.format(sql, ' AND '.join(param)) # sql_text = text(final_sql) results = fetch_with_filter(final_sql) # now we can run the query return results 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 not c.name 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 def closeEvent(self, event): ''' Raised when a request to close the window is received. Check the dirty state of input controls and prompt user to save if dirty. ''' event.accept() def cancel(self): ''' Slot for closing the dialog. Checks the dirty state first before closing. ''' self.reject()
class GroupSelectParameterWidget(GenericParameterWidget): """Widget class for Group Select Parameter.""" def __init__(self, parameter, parent=None): """Constructor. :param parameter: A GroupSelectParameter object. :type parameter: GroupSelectParameter """ QWidget.__init__(self, parent) self._parameter = parameter # Store spin box self.spin_boxes = {} # Create elements # Label (name) self.label = QLabel(self._parameter.name) # Layouts self.main_layout = QVBoxLayout() self.input_layout = QVBoxLayout() # _inner_input_layout must be filled with widget in the child class self.inner_input_layout = QVBoxLayout() self.radio_button_layout = QGridLayout() # Create radio button group self.input_button_group = QButtonGroup() # List widget self.list_widget = QListWidget() self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget.setDragDropMode(QAbstractItemView.DragDrop) self.list_widget.setDefaultDropAction(Qt.MoveAction) self.list_widget.setEnabled(False) self.list_widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) for i, key in enumerate(self._parameter.options): value = self._parameter.options[key] radio_button = QRadioButton(value.get('label')) self.radio_button_layout.addWidget(radio_button, i, 0) if value.get('type') == SINGLE_DYNAMIC: percentage_spin_box = PercentageSpinBox(self) self.radio_button_layout.addWidget(percentage_spin_box, i, 1) percentage_spin_box.setValue(value.get('value', 0)) step = percentage_spin_box.singleStep() if step > 1: precision = 0 else: precision = len(str(step).split('.')[1]) if precision > 3: precision = 3 percentage_spin_box.setDecimals(precision) self.spin_boxes[key] = percentage_spin_box # Enable spin box depends on the selected option if self._parameter.selected == key: percentage_spin_box.setEnabled(True) else: percentage_spin_box.setEnabled(False) elif value.get('type') == STATIC: static_value = value.get('value', 0) if static_value is not None: self.radio_button_layout.addWidget( QLabel(str(static_value * 100) + ' %'), i, 1) elif value.get('type') == MULTIPLE_DYNAMIC: if self._parameter.selected == key: self.list_widget.setEnabled(True) else: self.list_widget.setEnabled(False) self.input_button_group.addButton(radio_button, i) if self._parameter.selected == key: radio_button.setChecked(True) # Help text self.help_label = QLabel(self._parameter.help_text) self.help_label.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) self.help_label.setWordWrap(True) self.help_label.setAlignment(Qt.AlignTop) self.inner_input_layout.addLayout(self.radio_button_layout) self.inner_input_layout.addWidget(self.list_widget) # Put elements into layouts self.input_layout.addWidget(self.label) self.input_layout.addLayout(self.inner_input_layout) self.help_layout = QVBoxLayout() self.help_layout.addWidget(self.help_label) self.main_layout.addLayout(self.input_layout) self.main_layout.addLayout(self.help_layout) self.setLayout(self.main_layout) self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) # Update list widget self.update_list_widget() # Connect signal # noinspection PyUnresolvedReferences self.input_button_group.buttonClicked.connect( self.radio_buttons_clicked) def get_parameter(self): """Obtain list parameter object from the current widget state. :returns: A DefaultValueParameter from the current state of widget :rtype: DefaultValueParameter """ # Set value for each key for key, value in list(self._parameter.options.items()): if value.get('type') == STATIC: continue elif value.get('type') == SINGLE_DYNAMIC: new_value = self.spin_boxes.get(key).value() self._parameter.set_value_for_key(key, new_value) elif value.get('type') == MULTIPLE_DYNAMIC: # Need to iterate through all items items = [] for index in range(self.list_widget.count()): items.append(self.list_widget.item(index)) new_value = [i.text() for i in items] self._parameter.set_value_for_key(key, new_value) # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id == -1: self._parameter.selected = None else: self._parameter.selected = list( self._parameter.options.keys())[radio_button_checked_id] return self._parameter def update_list_widget(self): """Update list widget when radio button is clicked.""" # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id > -1: selected_dict = list( self._parameter.options.values())[radio_button_checked_id] if selected_dict.get('type') == MULTIPLE_DYNAMIC: for field in selected_dict.get('value'): # Update list widget field_item = QListWidgetItem(self.list_widget) field_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) field_item.setData(Qt.UserRole, field) field_item.setText(field) self.list_widget.addItem(field_item) def radio_buttons_clicked(self): """Handler when selected radio button changed.""" # Disable all spin boxes for spin_box in list(self.spin_boxes.values()): spin_box.setEnabled(False) # Disable list widget self.list_widget.setEnabled(False) # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() if radio_button_checked_id > -1: selected_value = list( self._parameter.options.values())[radio_button_checked_id] if selected_value.get('type') == MULTIPLE_DYNAMIC: # Enable list widget self.list_widget.setEnabled(True) elif selected_value.get('type') == SINGLE_DYNAMIC: selected_key = list( self._parameter.options.keys())[radio_button_checked_id] self.spin_boxes[selected_key].setEnabled(True) def select_radio_button(self, key): """Helper to select a radio button with key. :param key: The key of the radio button. :type key: str """ key_index = list(self._parameter.options.keys()).index(key) radio_button = self.input_button_group.button(key_index) radio_button.click()
class GroupSelectParameterWidget(GenericParameterWidget): """Widget class for Group Select Parameter.""" def __init__(self, parameter, parent=None): """Constructor. :param parameter: A GroupSelectParameter object. :type parameter: GroupSelectParameter """ QWidget.__init__(self, parent) self._parameter = parameter # Store spin box self.spin_boxes = {} # Create elements # Label (name) self.label = QLabel(self._parameter.name) # Layouts self.main_layout = QVBoxLayout() self.input_layout = QVBoxLayout() # _inner_input_layout must be filled with widget in the child class self.inner_input_layout = QVBoxLayout() self.radio_button_layout = QGridLayout() # Create radio button group self.input_button_group = QButtonGroup() # List widget self.list_widget = QListWidget() self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_widget.setDragDropMode(QAbstractItemView.DragDrop) self.list_widget.setDefaultDropAction(Qt.MoveAction) self.list_widget.setEnabled(False) self.list_widget.setSizePolicy( QSizePolicy.Maximum, QSizePolicy.Expanding) for i, key in enumerate(self._parameter.options): value = self._parameter.options[key] radio_button = QRadioButton(value.get('label')) self.radio_button_layout.addWidget(radio_button, i, 0) if value.get('type') == SINGLE_DYNAMIC: percentage_spin_box = PercentageSpinBox(self) self.radio_button_layout.addWidget(percentage_spin_box, i, 1) percentage_spin_box.setValue(value.get('value', 0)) step = percentage_spin_box.singleStep() if step > 1: precision = 0 else: precision = len(str(step).split('.')[1]) if precision > 3: precision = 3 percentage_spin_box.setDecimals(precision) self.spin_boxes[key] = percentage_spin_box # Enable spin box depends on the selected option if self._parameter.selected == key: percentage_spin_box.setEnabled(True) else: percentage_spin_box.setEnabled(False) elif value.get('type') == STATIC: static_value = value.get('value', 0) if static_value is not None: self.radio_button_layout.addWidget( QLabel(str(static_value * 100) + ' %'), i, 1) elif value.get('type') == MULTIPLE_DYNAMIC: if self._parameter.selected == key: self.list_widget.setEnabled(True) else: self.list_widget.setEnabled(False) self.input_button_group.addButton(radio_button, i) if self._parameter.selected == key: radio_button.setChecked(True) # Help text self.help_label = QLabel(self._parameter.help_text) self.help_label.setSizePolicy( QSizePolicy.Maximum, QSizePolicy.Expanding) self.help_label.setWordWrap(True) self.help_label.setAlignment(Qt.AlignTop) self.inner_input_layout.addLayout(self.radio_button_layout) self.inner_input_layout.addWidget(self.list_widget) # Put elements into layouts self.input_layout.addWidget(self.label) self.input_layout.addLayout(self.inner_input_layout) self.help_layout = QVBoxLayout() self.help_layout.addWidget(self.help_label) self.main_layout.addLayout(self.input_layout) self.main_layout.addLayout(self.help_layout) self.setLayout(self.main_layout) self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) # Update list widget self.update_list_widget() # Connect signal # noinspection PyUnresolvedReferences self.input_button_group.buttonClicked.connect( self.radio_buttons_clicked) def get_parameter(self): """Obtain list parameter object from the current widget state. :returns: A DefaultValueParameter from the current state of widget :rtype: DefaultValueParameter """ # Set value for each key for key, value in list(self._parameter.options.items()): if value.get('type') == STATIC: continue elif value.get('type') == SINGLE_DYNAMIC: new_value = self.spin_boxes.get(key).value() self._parameter.set_value_for_key(key, new_value) elif value.get('type') == MULTIPLE_DYNAMIC: # Need to iterate through all items items = [] for index in range(self.list_widget.count()): items.append(self.list_widget.item(index)) new_value = [i.text() for i in items] self._parameter.set_value_for_key(key, new_value) # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id == -1: self._parameter.selected = None else: self._parameter.selected = list(self._parameter.options.keys())[ radio_button_checked_id] return self._parameter def update_list_widget(self): """Update list widget when radio button is clicked.""" # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id > -1: selected_dict = list(self._parameter.options.values())[ radio_button_checked_id] if selected_dict.get('type') == MULTIPLE_DYNAMIC: for field in selected_dict.get('value'): # Update list widget field_item = QListWidgetItem(self.list_widget) field_item.setFlags( Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) field_item.setData(Qt.UserRole, field) field_item.setText(field) self.list_widget.addItem(field_item) def radio_buttons_clicked(self): """Handler when selected radio button changed.""" # Disable all spin boxes for spin_box in list(self.spin_boxes.values()): spin_box.setEnabled(False) # Disable list widget self.list_widget.setEnabled(False) # Get selected radio button radio_button_checked_id = self.input_button_group.checkedId() if radio_button_checked_id > -1: selected_value = list(self._parameter.options.values())[ radio_button_checked_id] if selected_value.get('type') == MULTIPLE_DYNAMIC: # Enable list widget self.list_widget.setEnabled(True) elif selected_value.get('type') == SINGLE_DYNAMIC: selected_key = list(self._parameter.options.keys())[ radio_button_checked_id] self.spin_boxes[selected_key].setEnabled(True) def select_radio_button(self, key): """Helper to select a radio button with key. :param key: The key of the radio button. :type key: str """ key_index = list(self._parameter.options.keys()).index(key) radio_button = self.input_button_group.button(key_index) radio_button.click()
def __init__(self, parent=None, plugin=None): super().__init__(parent=parent) self.setupUi(self) self._plugin = plugin self.emitFiltersChanged = True # Set up sources (in 2 columns; layout is grid) checked = ['PSScene4Band'] row_total = floor(len(DAILY_ITEM_TYPES) / 2) row = col = 0 gl = QGridLayout(self.frameSources) gl.setContentsMargins(0, 0, 0, 0) for a, b in DAILY_ITEM_TYPES: # Strip ' Scene' to reduce horizontal width of 2-column layout, except for SkySat name = b.replace(' Scene', '') if b != "SkySat Scene" else b cb = QCheckBox(name, parent=self.frameSources) cb.setChecked(a in checked) cb.setProperty('api-name', a) cb.setToolTip(b) # noinspection PyUnresolvedReferences cb.stateChanged[int].connect(self.filtersChanged) gl.addWidget(cb, row, col) row += 1 if row > row_total: row = 0 col += 1 self.frameSources.setLayout(gl) # TODO: (Eventually) Add multi-date range widget with and/or selector # noinspection PyUnresolvedReferences self.startDateEdit.valueChanged['QDateTime'].connect( self.filtersChanged) # noinspection PyUnresolvedReferences self.startDateEdit.valueChanged['QDateTime'].connect( self.set_min_enddate) # noinspection PyUnresolvedReferences self.startDateEdit.valueChanged['QDateTime'].connect( self.change_date_vis) # noinspection PyUnresolvedReferences self.endDateEdit.valueChanged['QDateTime'].connect( self.filtersChanged) # noinspection PyUnresolvedReferences self.endDateEdit.valueChanged['QDateTime'].connect( self.set_max_startdate) # noinspection PyUnresolvedReferences self.endDateEdit.valueChanged['QDateTime'].connect( self.change_date_vis) # Setup datetime boxes current_day = QDateTime().currentDateTimeUtc() self.startDateEdit.setDateTime(current_day.addMonths(-3)) self.endDateEdit.setDateTime(current_day) # TODO: (Eventually) Add multi-field searching, with +/- operation # of adding new field/QLineEdit, without duplicates # noinspection PyUnresolvedReferences self.leStringIDs.textChanged['QString'].connect(self.filters_changed) self.rangeCloudCover = PlanetExplorerRangeSlider( title='Cloud cover', filter_key='cloud_cover', prefix='', suffix='%', minimum=0, maximum=100, low=0, high=100, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeCloudCover) self.rangeCloudCover.rangeChanged[float, float].connect( self.filters_changed) self.rangeAzimuth = PlanetExplorerRangeSlider( title='Sun Azimuth', filter_key='sun_azimuth', prefix='', suffix='°', minimum=0, maximum=360, low=0, high=360, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeAzimuth) self.rangeAzimuth.rangeChanged[float, float].connect( self.filters_changed) self.rangeElevation = PlanetExplorerRangeSlider( title='Sun Elevation', filter_key='sun_elevation', prefix='', suffix='°', minimum=0, maximum=90, low=0, high=90, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeElevation) self.rangeElevation.rangeChanged[float, float].connect( self.filters_changed) self.rangeViewAngle = PlanetExplorerRangeSlider( title='View Angle', filter_key='view_angle', prefix='', suffix='°', minimum=-25, maximum=25, low=0, high=25, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeViewAngle) self.rangeViewAngle.rangeChanged[float, float].connect( self.filters_changed) self.rangeGsd = PlanetExplorerRangeSlider( title='Ground Sample Distance', filter_key='gsd', prefix='', suffix='m', minimum=0, maximum=50, low=0, high=50, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeGsd) self.rangeGsd.rangeChanged[float, float].connect( self.filters_changed) self.rangeAnomalousPx = PlanetExplorerRangeSlider( title='Anomalous Pixels', filter_key='anomalous_pixels', prefix='', suffix='%', minimum=0, maximum=100, low=0, high=100, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeAnomalousPx) self.rangeAnomalousPx.rangeChanged[float, float].connect( self.filters_changed) self.rangeUsable = PlanetExplorerRangeSlider( title='Usable Pixels', filter_key='usable_data', prefix='', suffix='%', minimum=0, maximum=100, low=0, high=100, step=1, precision=1 ) # Layout's parent widget takes ownership self.frameRangeSliders.layout().addWidget(self.rangeUsable) self.rangeUsable.rangeChanged[float, float].connect( self.filters_changed) self.rangeAreaCoverage = PlanetExplorerRangeSlider( title='Area Coverage', filter_key='area_coverage', prefix='', suffix='%', minimum=0, maximum=100, low=0, high=100, step=1, precision=1 ) self.frameRangeSliders.layout().addWidget(self.rangeAreaCoverage) self.rangeAreaCoverage.rangeChanged[float, float].connect( self.filters_changed) # TODO: Add rest of range sliders # Ground control filter checkbox # noinspection PyUnresolvedReferences self.chkBxGroundControl.stateChanged[int].connect(self.filters_changed) # Access Filter checkbox # noinspection PyUnresolvedReferences self.chkBxCanDownload.stateChanged[int].connect(self.filters_changed)
def setup_thresholds_panel(self, classification): """Setup threshold panel in the right panel. :param classification: Classification definition. :type classification: dict """ # Set text in the label layer_purpose = self.parent.step_kw_purpose.selected_purpose() layer_subcategory = self.parent.step_kw_subcategory.\ selected_subcategory() if is_raster_layer(self.parent.layer): active_band = self.parent.step_kw_band_selector.selected_band() layer_extent = self.parent.layer.extent() statistics = self.parent.layer.dataProvider().bandStatistics( active_band, QgsRasterBandStats.All, layer_extent, 0) description_text = continuous_raster_question % ( layer_purpose['name'], layer_subcategory['name'], classification['name'], statistics.minimumValue, statistics.maximumValue) else: field_name = self.parent.step_kw_field.selected_fields() field_index = self.parent.layer.fields().lookupField(field_name) min_value_layer = self.parent.layer.minimumValue(field_index) max_value_layer = self.parent.layer.maximumValue(field_index) description_text = continuous_vector_question % ( layer_purpose['name'], layer_subcategory['name'], field_name, classification['name'], min_value_layer, max_value_layer) # Set description description_label = QLabel(description_text) description_label.setWordWrap(True) self.right_layout.addWidget(description_label) if self.thresholds: thresholds = self.thresholds else: thresholds = self.parent.get_existing_keyword('thresholds') selected_unit = self.parent.step_kw_unit.selected_unit()['key'] self.threshold_classes = OrderedDict() classes = classification.get('classes') # Sort by value, put the lowest first classes = sorted(classes, key=lambda the_key: the_key['value']) grid_layout_thresholds = QGridLayout() for i, the_class in enumerate(classes): class_layout = QHBoxLayout() # Class label class_label = QLabel(the_class['name']) # Min label min_label = QLabel(tr('Min >')) # Min value as double spin min_value_input = QDoubleSpinBox() # TODO(IS) We can set the min and max depends on the unit, later min_value_input.setMinimum(0) min_value_input.setMaximum(999999) if thresholds.get(self.active_exposure['key']): exposure_thresholds = thresholds.get( self.active_exposure['key']) if exposure_thresholds.get(classification['key']): exposure_thresholds_classifications = exposure_thresholds\ .get(classification['key']) min_value_input.setValue( exposure_thresholds_classifications['classes'][ the_class['key']][0]) else: default_min = the_class['numeric_default_min'] if isinstance(default_min, dict): default_min = the_class[ 'numeric_default_min'][selected_unit] min_value_input.setValue(default_min) else: default_min = the_class['numeric_default_min'] if isinstance(default_min, dict): default_min = the_class[ 'numeric_default_min'][selected_unit] min_value_input.setValue(default_min) min_value_input.setSingleStep(0.1) # Max label max_label = QLabel(tr('Max <=')) # Max value as double spin max_value_input = QDoubleSpinBox() # TODO(IS) We can set the min and max depends on the unit, later max_value_input.setMinimum(0) max_value_input.setMaximum(999999) if thresholds.get(self.active_exposure['key']): exposure_thresholds = thresholds.get( self.active_exposure['key']) if exposure_thresholds.get(classification['key']): exposure_thresholds_classifications = exposure_thresholds \ .get(classification['key']) max_value_input.setValue( exposure_thresholds_classifications['classes'][ the_class['key']][1]) else: default_max = the_class['numeric_default_max'] if isinstance(default_max, dict): default_max = the_class[ 'numeric_default_max'][selected_unit] max_value_input.setValue(default_max) else: default_max = the_class['numeric_default_max'] if isinstance(default_max, dict): default_max = the_class[ 'numeric_default_max'][selected_unit] max_value_input.setValue(default_max) max_value_input.setSingleStep(0.1) # Add to class_layout class_layout.addWidget(min_label) class_layout.addWidget(min_value_input) class_layout.addWidget(max_label) class_layout.addWidget(max_value_input) class_layout.setStretch(0, 1) class_layout.setStretch(1, 2) class_layout.setStretch(2, 1) class_layout.setStretch(3, 2) # Add to grid_layout grid_layout_thresholds.addWidget(class_label, i, 0) grid_layout_thresholds.addLayout(class_layout, i, 1) self.threshold_classes[the_class['key']] = [ min_value_input, max_value_input] grid_layout_thresholds.setColumnStretch(0, 1) grid_layout_thresholds.setColumnStretch(0, 2) def min_max_changed(double_spin_index, mode): """Slot when min or max value change. :param double_spin_index: The index of the double spin. :type double_spin_index: int :param mode: The flag to indicate the min or max value. :type mode: int """ if mode == MAX_VALUE_MODE: current_max_value = list(self.threshold_classes.values())[ double_spin_index][1] target_min_value = list(self.threshold_classes.values())[ double_spin_index + 1][0] if current_max_value.value() != target_min_value.value(): target_min_value.setValue(current_max_value.value()) elif mode == MIN_VALUE_MODE: current_min_value = list(self.threshold_classes.values())[ double_spin_index][0] target_max_value = list(self.threshold_classes.values())[ double_spin_index - 1][1] if current_min_value.value() != target_max_value.value(): target_max_value.setValue(current_min_value.value()) # Set behaviour for k, v in list(self.threshold_classes.items()): index = list(self.threshold_classes.keys()).index(k) if index < len(self.threshold_classes) - 1: # Max value changed v[1].valueChanged.connect(partial( min_max_changed, double_spin_index=index, mode=MAX_VALUE_MODE)) if index > 0: # Min value v[0].valueChanged.connect(partial( min_max_changed, double_spin_index=index, mode=MIN_VALUE_MODE)) grid_layout_thresholds.setSpacing(0) self.right_layout.addLayout(grid_layout_thresholds)
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))
class EntityEditorDialog(MapperMixin): """ Dialog for editing entity attributes. """ addedModel = pyqtSignal(object) def __init__(self, entity, model=None, parent=None, manage_documents=True, collect_model=False, parent_entity=None, exclude_columns=None, plugin=None, allow_str_creation=True): """ Class constructor. :param entity: Entity object corresponding to a table object. :type entity: Entity :param model: Data object for loading data into the form widgets. If the model is set, then the editor dialog is assumed to be in edit mode. :type model: object :param parent: Parent widget that the form belongs to. :type parent: QWidget :param manage_documents: True if the dialog should provide controls for managing supporting documents. Only applicable if the entity allows for supporting documents to be attached. :type manage_documents: bool :param collect_model: If set to True only returns the filled form model without saving it to the database. :type collect_model: Boolean :param parent_entity: The parent entity of the editor :type parent_entity: Object :param exclude_columns: List of columns to be excluded if in a list. :type exclude_columns: List :return: If collect_model, returns SQLAlchemy Model """ super().__init__(parent=parent, model=model, entity=entity) QgsGui.enableAutoGeometryRestore(self) self.collection_suffix = self.tr('Collection') # Set minimum width self.setMinimumWidth(450) self.plugin = plugin # Flag for mandatory columns self.has_mandatory = False self.reload_form = False self._entity = entity self.edit_model = model self.column_widgets = OrderedDict() self.columns = {} self._parent = parent self.exclude_columns = exclude_columns or [] self.entity_tab_widget = None self._disable_collections = False self.filter_val = None self.parent_entity = parent_entity self.child_models = OrderedDict() self.entity_scroll_area = None self.entity_editor_widgets = OrderedDict() self.details_tree_view = None # Set notification layout bar self.vlNotification = QVBoxLayout() self.vlNotification.setObjectName('vlNotification') self._notifBar = NotificationBar(self.vlNotification) self.do_not_check_dirty = False # Set manage documents only if the entity supports documents if self._entity.supports_documents: self._manage_documents = manage_documents else: self._manage_documents = False # Setup entity model self._ent_document_model = None if self._entity.supports_documents: self.ent_model, self._ent_document_model = entity_model( self._entity, with_supporting_document=True) else: self.ent_model = entity_model(self._entity) if model is not None: self.ent_model = model MapperMixin.__init__(self, self.ent_model, entity) self.collect_model = collect_model self.register_column_widgets() try: if isinstance(parent._parent, EntityEditorDialog): # hide collections form child editor self._disable_collections = True except AttributeError: self._parent._parent = None # Set title editor_trans = self.tr('Editor') 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) self.title = '{0} {1}'.format(title_str, editor_trans) self.setWindowTitle(self.title) # determine whether the entity is part of the STR relationship curr_profile = current_profile() self.participates_in_str, self.is_party_unit = curr_profile.social_tenure.entity_participates_in_str( self.entity) self._init_gui(show_str_tab=allow_str_creation and self.participates_in_str, is_party_unit=self.is_party_unit) self.adjustSize() self._get_entity_editor_widgets() if isinstance(parent._parent, EntityEditorDialog): self.parent_entity = parent.parent_entity self.set_parent_values() # make the size smaller to differentiate from parent and as it # only has few tabs. self.adjustSize() self.attribute_mappers = self._attr_mapper_collection # Exception title for editor extension exceptions self._ext_exc_msg = self.tr( 'An error has occured while executing Python code in the editor ' 'extension:') # Register custom editor extension if specified self._editor_ext = entity_dlg_extension(self) if self._editor_ext is not None: self._editor_ext.post_init() # Initialize CascadingFieldContext objects self._editor_ext.connect_cf_contexts() def _init_gui(self, show_str_tab: bool, is_party_unit: bool): # Setup base elements self.gridLayout = QGridLayout(self) self.gridLayout.setObjectName('glMain') self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1) # set widgets values column_widget_area = self._setup_columns_content_area() self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1) if show_str_tab: self._setup_str_tab(is_party_unit=is_party_unit) # Add notification for mandatory columns if applicable next_row = 2 if self.has_mandatory: self.required_fields_lbl = QLabel(self) msg = self.tr('Please fill out all required (*) fields.') msg = self._highlight_asterisk(msg) self.required_fields_lbl.setText(msg) self.gridLayout.addWidget(self.required_fields_lbl, next_row, 0, 1, 2) # Bump up row reference next_row += 1 self.buttonBox = QDialogButtonBox(self) self.buttonBox.setObjectName('buttonBox') self.gridLayout.addWidget(self.buttonBox, next_row, 0, 1, 1) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Save) if self.edit_model is None: if not self.collect_model: self.save_new_button = QPushButton( QApplication.translate('EntityEditorDialog', 'Save and New')) self.buttonBox.addButton(self.save_new_button, QDialogButtonBox.ActionRole) # edit model, collect model # adding new record for child # Saving in parent editor if not isinstance(self._parent._parent, EntityEditorDialog): # adding a new record if self.edit_model is None: # saving when digitizing. if self.collect_model: self.buttonBox.accepted.connect(self.on_model_added) # saving parent editor else: self.buttonBox.accepted.connect(self.save_parent_editor) self.save_new_button.clicked.connect(self.save_and_new) # updating existing record else: if not self.collect_model: # updating existing record of the parent editor self.buttonBox.accepted.connect(self.save_parent_editor) else: self.buttonBox.accepted.connect(self.on_model_added) # Saving in child editor else: # save and new record if self.edit_model is None: self.buttonBox.accepted.connect(self.on_child_saved) self.save_new_button.clicked.connect( lambda: self.on_child_saved(True)) else: # When updating an existing child editor save to the db self.buttonBox.accepted.connect(self.on_child_saved) # self.buttonBox.accepted.connect(self.submit) self.buttonBox.rejected.connect(self.cancel) @property def notification_bar(self): """ :return: Returns the dialog's notification bar. :rtype: NotificationBar """ return self._notifBar def save_parent_editor(self): """ Saves the parent editor and its children. """ self.submit() self.save_children() def set_parent_values(self): """ Sets the parent display column for the child. """ if self.parent_entity is None: return for col in self._entity.columns.values(): if col.TYPE_INFO == 'FOREIGN_KEY': parent_entity = col.parent if parent_entity == self.parent_entity: self.parent_widgets_value_setter(self._parent._parent, col) def parent_widgets_value_setter(self, parent, col): """ Finds and sets the value from parent widget and set it to the column widget of a child using the child column. :param parent: The parent widget :type parent: QWidget :param col: The child column object :type col: Object """ for parent_col_name, parent_widget in parent.column_widgets.items(): parent_col = parent.columns[parent_col_name] if parent_col.name == col.name: self.single_parent_value_setter(col, parent_widget) break if parent_col.name in col.entity_relation.display_cols: self.single_parent_value_setter(col, parent_widget) break def single_parent_value_setter(self, col, parent_widget): """ Gets value from parent widget and set it to the column widget of a child using the child column. :param parent: The parent widget :type parent: QWidget :param col: The child column object :type col: Object """ local_widget = self.column_widgets[col.name] local_widget.show_clear_button() self.filter_val = parent_widget.text() local_widget.setText(self.filter_val) def save_and_new(self): """ A slot raised when Save and New button is click. It saves the form without showing a success message. Then it sets reload_form property to True so that entity_browser can re-load the form. """ from stdm.ui.entity_browser import (EntityBrowserWithEditor) self.submit(False, True) self.save_children() if self.is_valid: self.addedModel.emit(self.model()) self.setModel(self.ent_model()) self.clear() self.child_models.clear() for index in range(0, self.entity_tab_widget.count() - 1): if isinstance(self.entity_tab_widget.widget(index), EntityBrowserWithEditor): child_browser = self.entity_tab_widget.widget(index) child_browser.remove_rows() def on_model_added(self): """ A slot raised when a form is submitted with collect model set to True. There will be no success message and the form does not close. """ self.submit(True) self.addedModel.emit(self.model()) def closeEvent(self, event): """ Raised when a request to close the window is received. Check the dirty state of input controls and prompt user to save if dirty. """ if self.do_not_check_dirty: event.accept() return isDirty, userResponse = self.checkDirty() if isDirty: if userResponse == QMessageBox.Yes: # We need to ignore the event so that validation and # saving operations can be executed event.ignore() self.submit() elif userResponse == QMessageBox.No: event.accept() elif userResponse == QMessageBox.Cancel: event.ignore() else: event.accept() def on_child_saved(self, save_and_new=False): """ A slot raised when the save or save and new button is clicked. It sets the child_models dictionary of the parent when saved. :param save_and_new: A boolean indicating the save and new button is clicked to trigger the slot. :type save_and_new: Boolean """ if self.parent_entity is None: return self.submit(True) insert_pos = self._parent.tbEntity.model().rowCount() + 1 # Save to parent editor so that it is persistent. self._parent._parent.child_models[insert_pos, self._entity.name] = \ (self._entity, self.model()) self.addedModel.emit(self.model()) if not save_and_new: self.accept() else: if self.is_valid: # self.addedModel.emit(self.model()) self.setModel(self.ent_model()) self.clear() self.set_parent_values() def save_children(self): """ Saves children models into the database by assigning the the id of the parent for foreign key column. """ if len(self.child_models) < 1: return children_obj = [] for row_entity, row_value in self.child_models.items(): entity, model = row_value ent_model = entity_model(entity) entity_obj = ent_model() for col in entity.columns.values(): if col.TYPE_INFO == 'FOREIGN_KEY': if col.parent.name == self._entity.name: setattr(model, col.name, self.model().id) children_obj.append(model) entity_obj.saveMany(children_obj) def register_column_widgets(self): """ Registers the column widgets. """ # Append column labels and widgets table_name = self._entity.name columns = table_column_names(table_name) self.scroll_widget_contents = QWidget() self.scroll_widget_contents.setObjectName('scrollAreaWidgetContents') for c in self._entity.columns.values(): if c.name in self.exclude_columns: continue if c.name not in columns and not isinstance(c, VirtualColumn): continue # Get widget factory column_widget = ColumnWidgetRegistry.create( c, self.scroll_widget_contents, host=self) self.columns[c.name] = c self.column_widgets[c.name] = column_widget 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 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) # Format label text if it is a mandatory field if c.mandatory: header = '{0} *'.format(c.ui_display()) # Highlight asterisk header = self._highlight_asterisk(header) self.c_label.setText(header) self.gl.addWidget(self.c_label, row_id, 0, 1, 1) self.column_widget = column_widget self.gl.addWidget(self.column_widget, row_id, 1, 1, 1) # Add user tip if specified for the column configuration if c.user_tip: self.tip_lbl = UserTipLabel(user_tip=c.user_tip) self.gl.addWidget(self.tip_lbl, row_id, 2, 1, 1) if c.mandatory and not self.has_mandatory: self.has_mandatory = True col_name = c.name # Replace name accordingly based on column type if isinstance(c, MultipleSelectColumn): col_name = c.model_attribute_name # Add widget to MapperMixin collection self.addMapping(col_name, self.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() if not self._disable_collections: ch_entities = self.children_entities() for col, ch in ch_entities: if hasattr(col.entity_relation, 'show_in_parent'): if col.entity_relation.show_in_parent != '0': self._add_fk_browser(ch, col) else: self._add_fk_browser(ch, col) # Add tab widget if entity supports documents if self._entity.supports_documents: self.doc_widget = SupportingDocumentsWidget( self._entity.supporting_doc, self._ent_document_model, self) # Map the source document manager object self.addMapping('documents', self.doc_widget.source_document_manager) # # # Add attribute tab # self._add_primary_attr_widget() # Add supporting documents tab self.entity_tab_widget.addTab(self.doc_widget, self.tr('Supporting Documents')) # Return the correct widget if self.entity_tab_widget is not None: return self.entity_tab_widget return self.entity_scroll_area def _add_primary_attr_widget(self): # Check if the primary entity # exists and add if it does not pr_txt = self.tr('Primary') if self.entity_tab_widget is not None: tab_txt = self.entity_tab_widget.tabText(0) if not tab_txt == pr_txt: self.entity_tab_widget.addTab(self.entity_scroll_area, pr_txt) def _setup_str_tab(self, is_party_unit: bool): """ Creates the STR relationship tab """ from stdm.ui.feature_details import DetailsTreeView layout = QVBoxLayout() hl = QHBoxLayout() add_btn = QToolButton(self) add_btn.setText(self.tr('Create STR')) add_btn.setIcon(GuiUtils.get_icon('add.png')) hl.addWidget(add_btn) add_btn.clicked.connect(self._create_str) edit_btn = QToolButton(self) edit_btn.setText(self.tr('Edit')) edit_btn.setIcon(GuiUtils.get_icon('edit.png')) edit_btn.setDisabled(True) hl.addWidget(edit_btn) view_document_btn = QToolButton(self) view_document_btn.setText(self.tr('View Supporting Documents')) view_document_btn.setIcon(GuiUtils.get_icon('document.png')) view_document_btn.setDisabled(True) hl.addWidget(view_document_btn) hl.addStretch() layout.addLayout(hl) self.details_tree_view = DetailsTreeView( parent=self, plugin=self.plugin, edit_button=edit_btn, view_document_button=view_document_btn) self.details_tree_view.activate_feature_details( True, follow_layer_selection=False) self.details_tree_view.model.clear() if is_party_unit: self.details_tree_view.search_party(self.entity, [self.ent_model.id]) else: self.details_tree_view.search_spatial_unit(self.entity, [self.ent_model.id]) layout.addWidget(self.details_tree_view) w = QWidget() w.setLayout(layout) self.entity_tab_widget.addTab(w, self.tr('STR')) def _create_str(self): """ Opens the dialog to create a new STR """ from stdm.ui.social_tenure.str_editor import STREditor add_str_window = STREditor() #if is_party_unit: # add_str.set_party_data(self.entity_model_obj) if add_str_window.exec_(): # STR created - refresh STR view if self.is_party_unit: self.details_tree_view.search_party(self.entity, [self.ent_model.id]) else: self.details_tree_view.search_spatial_unit( self.entity, [self.ent_model.id]) def _add_fk_browser(self, child_entity, column): # Create and add foreign key # browser to the collection from stdm.ui.entity_browser import (ContentGroupEntityBrowser) attr = '{0}_collection'.format(child_entity.name) # Return if the attribute does not exist if not hasattr(self._model, attr): return table_content = TableContentGroup(User.CURRENT_USER.UserName, child_entity.short_name) if self.edit_model is not None: parent_id = self.edit_model.id else: parent_id = 0 entity_browser = ContentGroupEntityBrowser(child_entity, table_content, rec_id=parent_id, parent=self, plugin=self.plugin, load_recs=False) # entity_browser = EntityBrowserWithEditor( # child_entity, # self, # MANAGE, # False, # plugin=self.plugin # ) entity_browser.buttonBox.setVisible(False) entity_browser.record_filter = [] if len(child_entity.label) > 2: column_label = child_entity.label else: # Split and join to filter out entity name prefix # e.g. 'lo_parcel' to 'parcel' column_label = format_name(" ".join( child_entity.name.split("_", 1)[1:])) self.set_filter(child_entity, entity_browser) self.entity_tab_widget.addTab(entity_browser, '{0}'.format(column_label)) def set_filter(self, entity, browser): col = self.filter_col(entity) child_model = entity_model(entity) child_model_obj = child_model() col_obj = getattr(child_model, col.name) browser.filtered_records = [] if self.model() is not None: if self.model().id is not None: browser.filtered_records = child_model_obj.queryObject( ).filter(col_obj == self.model().id).all() if self.edit_model is not None: browser.filtered_records = child_model_obj.queryObject().filter( col_obj == self.edit_model.id).all() def filter_col(self, child_entity): for col in child_entity.columns.values(): if col.TYPE_INFO == 'FOREIGN_KEY': parent_entity = col.parent if parent_entity == self._entity: return col def children_entities(self): """ :return: Returns a list of children entities (by name) that refer to the main entity as the parent. :rtype: OrderedDict """ child_columns = [] for ch in self._entity.children(): if ch.TYPE_INFO == Entity.TYPE_INFO: for col in ch.columns.values(): if hasattr(col, 'entity_relation'): if col.parent.name == self._entity.name: child_columns.append((col, ch)) return child_columns def document_widget(self): """ :return: Returns the widget for managing the supporting documents for an entity if enabled. :rtype: SupportingDocumentsWidget """ return self.doc_widget def source_document_manager(self): """ :return: Returns an instance of the SourceDocumentManager only if supporting documents are enabled for the given entity. Otherwise, None if supporting documents are not enabled. :rtype: SourceDocumentManager """ if self.doc_widget is None: return None return self.doc_widget.source_document_manager def _highlight_asterisk(self, text): # Highlight asterisk in red c = '*' # Do not format if there is no asterisk if text.find(c) == -1: return text asterisk_highlight = '<span style=\" color:#ff0000;\">*</span>' text = text.replace(c, asterisk_highlight) return '<html><head/><body><p>{0}</p></body></html>'.format(text) def _custom_validate(self): """ Override of the MapperMixin which enables custom editor extensions to inject additional validation before saving form data. :return: Return True if the validation was successful, otherwise False. :rtype: bool """ if self._editor_ext is not None: return self._editor_ext.validate() # Return True if there is no custom editor extension specified return True def _post_save(self, model): """ Include additional post-save logic by custom extensions. :param model: SQLAlchemy model :type model: object """ if self._editor_ext is not None: self._editor_ext.post_save(model) def _get_entity_editor_widgets(self): """ Gets entity editor widgets and appends them to a dictionary """ if self.entity_tab_widget: tab_count = self.entity_tab_widget.count() for i in range(tab_count): tab_object = self.entity_tab_widget.widget(i) tab_text = self.entity_tab_widget.tabText(i) self.entity_editor_widgets[tab_text] = tab_object else: self.entity_editor_widgets['no_tab'] = self.entity_scroll_area
def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr('Rectangle size')) self.width = QLineEdit() self.height = QLineEdit() width_val = QDoubleValidator() height_val = QDoubleValidator() self.width.setValidator(width_val) self.height.setValidator(height_val) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) grid = QGridLayout() grid.addWidget(QLabel(tr('Give a size in m:')), 0, 0) grid.addWidget(QLabel(tr('Width:')), 1, 0) grid.addWidget(QLabel(tr('Height:')), 1, 1) grid.addWidget(self.width, 2, 0) grid.addWidget(self.height, 2, 1) grid.addWidget(buttons, 3, 0, 1, 2) self.setLayout(grid)
class ShellOutputScintilla(QgsCodeEditorPython): def __init__(self, parent=None): super().__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can harm your computer" ) # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def initializeLexer(self): super().initializeLexer() self.setFoldingVisible(False) self.setEdgeMode(QsciScintilla.EdgeNone) def refreshSettingsOutput(self): # Set Python lexer self.initializeLexer() self.setReadOnly(True) self.setCaretWidth(0) # NO (blinking) caret in the output def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) menu.addAction( QgsApplication.getThemeIcon("console/iconHideToolConsole.svg"), QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"), QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( QgsApplication.getThemeIcon("console/iconClearConsole.svg"), QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) pyQGISHelpAction = menu.addAction( QgsApplication.getThemeIcon("console/iconHelpConsole.svg"), QCoreApplication.translate("PythonConsole", "Search Selected in PyQGIS docs"), self.searchSelectedTextInPyQGISDocs) menu.addSeparator() copyAction = menu.addAction( QgsApplication.getThemeIcon("mActionEditCopy.svg"), QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction( QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"), QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) pyQGISHelpAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) pyQGISHelpAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
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 ReferencedTableEditor(QWidget): referenced_table_changed = pyqtSignal(str) def __init__(self, parent=None): QWidget.__init__(self, parent) self.setupUi() self.cbo_ref_table.setInsertPolicy(QComboBox.InsertAlphabetically) self.cbo_ref_table.currentIndexChanged[str].connect(self._on_ref_table_changed) # Tables that will be omitted from the referenced table list self._omit_ref_tables = [] 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.table_names() # Connect signals QMetaObject.connectSlotsByName(self) self.cbo_ref_table.currentIndexChanged[str].connect(self._load_source_table_fields) @pyqtSlot(str) 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 """ setComboCurrentIndexWithText(self.cbo_ref_table, table_props.linked_table) setComboCurrentIndexWithText(self.cbo_source_field, table_props.source_field) setComboCurrentIndexWithText(self.cbo_referencing_col, table_props.linked_field) 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)
def set_up_resource_parameters(self): """Set up the resource parameter for the add/edit view. """ name_parameter = StringParameter('UUID-1') name_parameter.name = self.resource_parameters['Resource name'] name_parameter.help_text = tr( 'Name of the resource that will be provided ' 'as part of minimum needs. ' 'e.g. Rice, Water etc.') name_parameter.description = tr( 'A <b>resource</b> is something that you provide to displaced ' 'persons in the event of a disaster. The resource will be made ' 'available at IDP camps and may need to be stockpiled by ' 'contingency planners in their preparations for a disaster.') name_parameter.is_required = True name_parameter.value = '' description_parameter = StringParameter('UUID-2') description_parameter.name = self.resource_parameters[ 'Resource description'] description_parameter.help_text = tr( 'Description of the resource that will be provided as part of ' 'minimum needs.') description_parameter.description = tr( 'This gives a detailed description of what the resource is and ') description_parameter.is_required = True description_parameter.value = '' unit_parameter = StringParameter('UUID-3') unit_parameter.name = self.resource_parameters['Unit'] unit_parameter.help_text = tr( 'Single unit for the resources spelled out. e.g. litre, ' 'kilogram etc.') unit_parameter.description = tr( 'A <b>unit</b> is the basic measurement unit used for computing ' 'the allowance per individual. For example when planning water ' 'rations the unit would be single litre.') unit_parameter.is_required = True unit_parameter.value = '' units_parameter = StringParameter('UUID-4') units_parameter.name = self.resource_parameters['Units'] units_parameter.help_text = tr( 'Multiple units for the resources spelled out. e.g. litres, ' 'kilogram etc.') units_parameter.description = tr( '<b>Units</b> are the basic measurement used for computing the ' 'allowance per individual. For example when planning water ' 'rations the units would be litres.') units_parameter.is_required = True units_parameter.value = '' unit_abbreviation_parameter = StringParameter('UUID-5') unit_abbreviation_parameter.name = \ self.resource_parameters['Unit abbreviation'] unit_abbreviation_parameter.help_text = tr( 'Abbreviations of unit for the resources. e.g. l, kg etc.') unit_abbreviation_parameter.description = tr( "A <b>unit abbreviation</b> is the basic measurement unit's " "shortened. For example when planning water rations " "the units would be l.") unit_abbreviation_parameter.is_required = True unit_abbreviation_parameter.value = '' minimum_parameter = FloatParameter('UUID-6') minimum_parameter.name = self.resource_parameters['Minimum allowed'] minimum_parameter.is_required = True minimum_parameter.precision = 6 minimum_parameter.minimum_allowed_value = 0.0 minimum_parameter.maximum_allowed_value = 99999.0 minimum_parameter.help_text = tr( 'The minimum allowable quantity per person. ') minimum_parameter.description = tr( 'The <b>minimum</b> is the minimum allowed quantity of the ' 'resource per person. For example you may dictate that the water ' 'ration per person per day should never be allowed to be less ' 'than 0.5l. This is enforced when tweaking a minimum needs set ' 'before an impact evaluation') minimum_parameter.value = 0.00 maximum_parameter = FloatParameter('UUID-7') maximum_parameter.name = self.resource_parameters['Maximum allowed'] maximum_parameter.is_required = True maximum_parameter.precision = 6 maximum_parameter.minimum_allowed_value = 0.0 maximum_parameter.maximum_allowed_value = 99999.0 maximum_parameter.help_text = tr( 'The maximum allowable quantity per person. ') maximum_parameter.description = tr( 'The <b>maximum</b> is the maximum allowed quantity of the ' 'resource per person. For example you may dictate that the water ' 'ration per person per day should never be allowed to be more ' 'than 67l. This is enforced when tweaking a maximum needs set ' 'before an impact evaluation.') maximum_parameter.value = 100.0 default_parameter = FloatParameter('UUID-8') default_parameter.name = self.resource_parameters['Default'] default_parameter.is_required = True default_parameter.precision = 6 default_parameter.minimum_allowed_value = 0.0 default_parameter.maximum_allowed_value = 99999.0 default_parameter.help_text = tr( 'The default allowable quantity per person. ') default_parameter.description = tr( "The <b>default</b> is the default allowed quantity of the " "resource per person. For example you may indicate that the water " "ration per person weekly should be 67l.") default_parameter.value = 10.0 frequency_parameter = StringParameter('UUID-9') frequency_parameter.name = self.resource_parameters['Frequency'] frequency_parameter.help_text = tr( "The frequency that this resource needs to be provided to a " "displaced person. e.g. weekly, daily, once etc.") frequency_parameter.description = tr( "The <b>frequency</b> informs the aid worker how regularly this " "resource needs to be provided to the displaced person.") frequency_parameter.is_required = True frequency_parameter.value = tr('weekly') sentence_parameter = TextParameter('UUID-10') sentence_parameter.name = self.resource_parameters['Readable sentence'] sentence_parameter.help_text = tr( 'A readable presentation of the resource.') sentence_parameter.description = tr( "A <b>readable sentence</b> is a presentation of the resource " "that displays all pertinent information. If you are unsure then " "use the default. Properties should be included using double " "curly brackets '{{' '}}'. Including the resource name would be " "achieved by including e.g. {{ Resource name }}") sentence_parameter.is_required = True sentence_parameter.value = tr( 'A displaced person should be provided with ' '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of ' '%(resource name)s. Though no less than %(minimum allowed)s ' 'and no more than %(maximum allowed)s. This should be provided ' '%(frequency)s.' % { 'default value': '{{ Default }}', 'unit': '{{ Unit }}', 'units': '{{ Units }}', 'unit abbreviation': '{{ Unit abbreviation }}', 'resource name': '{{ Resource name }}', 'minimum allowed': '{{ Minimum allowed }}', 'maximum allowed': '{{ Maximum allowed }}', 'frequency': '{{ Frequency }}' }) parameters = [ name_parameter, description_parameter, unit_parameter, units_parameter, unit_abbreviation_parameter, default_parameter, minimum_parameter, maximum_parameter, frequency_parameter, sentence_parameter ] parameter_container = ParameterContainer(parameters) parameter_container.setup_ui() layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(parameter_container) self.parameters_scrollarea.setLayout(layout)
def main(): """Main function to run the example.""" from safe.test.utilities import get_qgis_app QGIS_APP, CANVAS, IFACE, PARENT = get_qgis_app(qsetting=INASAFE_TEST) options = OrderedDict([ (DO_NOT_REPORT, { 'label': 'Do not report', 'value': None, 'type': STATIC, 'constraint': {} }), (GLOBAL_DEFAULT, { 'label': 'Global default', 'value': 0.5, 'type': STATIC, 'constraint': {} }), ( CUSTOM_VALUE, { 'label': 'Custom', 'value': 0.7, # Taken from keywords / recent value 'type': SINGLE_DYNAMIC, 'constraint': { 'min': 0, 'max': 1 } }), ( FIELDS, { 'label': 'Ratio fields', 'value': ['field A', 'field B', 'field C'], # Taken from keywords 'type': MULTIPLE_DYNAMIC, 'constraint': {} }) ]) default_value_parameter = GroupSelectParameter() default_value_parameter.name = 'Group Select parameter' default_value_parameter.help_text = 'Help text' default_value_parameter.description = 'Description' default_value_parameter.options = options default_value_parameter.selected = 'ratio fields' parameters = [default_value_parameter] extra_parameters = [(GroupSelectParameter, GroupSelectParameterWidget)] parameter_container = ParameterContainer(parameters, extra_parameters=extra_parameters) parameter_container.setup_ui() widget = QWidget() layout = QGridLayout() layout.addWidget(parameter_container) def show_error_message(parent, exception): """Generate error message to handle parameter errors :param parent: The widget as a parent of message box :type parent: QWidget :param exception: python Exception or Error :type exception: Exception """ box = QMessageBox() box.critical(parent, 'Error occured', str(exception)) def show_parameter(the_parameter_container): """Print the value of parameter. :param the_parameter_container: A parameter container :type the_parameter_container: ParameterContainer """ def show_parameter_value(a_parameter): if isinstance(a_parameter, GroupParameter): for param in a_parameter.value: show_parameter_value(param) else: # print(a_parameter.guid, a_parameter.name, a_parameter.value) pass try: the_parameters = the_parameter_container.get_parameters() if the_parameters: for parameter in the_parameters: show_parameter_value(parameter) except Exception as inst: show_error_message(parameter_container, inst) print_button = QPushButton('Print Result') # noinspection PyUnresolvedReferences print_button.clicked.connect( partial(show_parameter, the_parameter_container=parameter_container)) layout.addWidget(print_button) widget.setLayout(layout) widget.setGeometry(0, 0, 500, 500) widget.show() sys.exit(QGIS_APP.exec_())
def __init__(self, layer_a, layer_b, pca_layers, pca_stats, nodata=None): QDialog.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose, True) self.layer_a = layer_a self.layer_b = layer_b self.pca_layers = pca_layers MainAnalysisDialog.pca_layers = pca_layers MainAnalysisDialog.pca_stats = pca_stats MainAnalysisDialog.nodata = nodata # flags self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) self.setupUi(self) # dialog buttons box self.CloseButton.clicked.connect(self.closing) # return self.ReturnToMainDialog.clicked.connect(self.return_to_main_dialog) # save all components in a stack self.SavePCA.clicked.connect(self.save_pca) if self.layer_a is None: self.SavePCA.setDisabled(True) self.SavePCA.setToolTip("Not available if the components are loaded externally") # merge change layer self.OpenMergeChangeLayers.clicked.connect(self.open_merge_change_layers) # size of the grid with view render widgets windows if self.layer_b is not None: if len(pca_layers) <= 4: grid_rows = 2 grid_columns = 4 elif len(pca_layers) <= 8: grid_rows = 3 grid_columns = 4 elif len(pca_layers) <= 12: grid_rows = 4 grid_columns = 4 elif len(pca_layers) <= 16: grid_rows = 5 grid_columns = 4 elif len(pca_layers) <= 20: grid_rows = 5 grid_columns = 5 if self.layer_b is None: if len(pca_layers) <= 3: grid_rows = 2 grid_columns = 3 elif len(pca_layers) <= 6: grid_rows = 3 grid_columns = 3 elif len(pca_layers) <= 9: grid_rows = 4 grid_columns = 3 elif len(pca_layers) <= 12: grid_rows = 4 grid_columns = 4 elif len(pca_layers) <= 16: grid_rows = 5 grid_columns = 4 elif len(pca_layers) <= 20: grid_rows = 5 grid_columns = 5 # configure the views layout views_layout = QGridLayout() views_layout.setSpacing(0) views_layout.setMargin(0) view_widgets = [] for row in range(grid_rows): if row == 0: for column in range(grid_columns): new_view_widget = LayerViewWidget() views_layout.addWidget(new_view_widget, row, column) view_widgets.append(new_view_widget) else: for column in range(grid_columns): if (row-1)*grid_columns + column < len(pca_layers): new_view_widget = LayerViewWidget() views_layout.addWidget(new_view_widget, row, column) view_widgets.append(new_view_widget) # add to change analysis dialog self.widget_view_windows.setLayout(views_layout) # save instances MainAnalysisDialog.view_widgets = view_widgets # setup view widget for idx, view_widget in enumerate(MainAnalysisDialog.view_widgets, start=1): view_widget.id = idx view_widget.setup_view_widget(crs=self.pca_layers[0].crs()) # set views for num_view, view_widget in enumerate(MainAnalysisDialog.view_widgets, start=1): if num_view == 2 and self.layer_a is not None: view_widget.QLabel_ViewName.setText("Layer A") file_index = view_widget.QCBox_RenderFile.findText(self.layer_a.name(), Qt.MatchFixedString) view_widget.QCBox_RenderFile.setCurrentIndex(file_index) if num_view == 3 and self.layer_b is not None: view_widget.QLabel_ViewName.setText("Layer B") file_index = view_widget.QCBox_RenderFile.findText(self.layer_b.name(), Qt.MatchFixedString) view_widget.QCBox_RenderFile.setCurrentIndex(file_index) if grid_columns < num_view <= len(self.pca_layers)+grid_columns: view_widget.pc_id = num_view-grid_columns view_widget.QLabel_ViewName.setText("Principal Component {}".format(view_widget.pc_id)) file_index = view_widget.QCBox_RenderFile.findText(self.pca_layers[num_view-grid_columns-1].name(), Qt.MatchFixedString) view_widget.WidgetDetectionLayer.setEnabled(True) view_widget.QCBox_RenderFile.setCurrentIndex(file_index) view_widget.QCBox_RenderFile.setEnabled(False) view_widget.QCBox_browseRenderFile.setEnabled(False) # disconnect set_render_layer signal view_widget.QCBox_RenderFile.currentIndexChanged.disconnect() else: view_widget.QCBox_RenderFile.setExceptedLayerList(self.pca_layers) # hide pca layers in combobox menu view_widget.EnableChangeDetection.setToolTip("Show/hide the merged change layer") view_widget.QPBtn_ComponentAnalysisDialog.setText("Merged change layer") view_widget.QPBtn_ComponentAnalysisDialog.setToolTip("The merged change layer has not been generated yet") # disconnect button action view_widget.QPBtn_ComponentAnalysisDialog.clicked.disconnect() if not view_widget.QLabel_ViewName.text(): view_widget.QLabel_ViewName.setPlaceholderText("Auxiliary View") self.MsgBar.pushMessage("{} principal components were generated and loaded successfully".format(len(self.pca_layers)), level=Qgis.Success)
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 GwCatalog: def __init__(self): """ Class to control toolbar 'om_ws' """ self.controller = global_vars.controller def api_catalog(self, previous_dialog, widget_name, geom_type, feature_type): # Manage if geom_type is gully and set grate if geom_type == 'gully': geom_type = 'grate' form_name = 'upsert_catalog_' + geom_type + '' form = f'"formName":"{form_name}", "tabName":"data", "editable":"TRUE"' feature = f'"feature_type":"{feature_type}"' body = create_body(form, feature) sql = f"SELECT gw_fct_getcatalog({body})::text" row = self.controller.get_row(sql) if not row: self.controller.show_message("NOT ROW FOR: " + sql, 2) return complet_list = [json.loads(row[0], object_pairs_hook=OrderedDict)] groupBox_1 = QGroupBox("Filter") self.filter_form = QGridLayout() self.dlg_catalog = InfoCatalogUi() load_settings(self.dlg_catalog) self.dlg_catalog.btn_cancel.clicked.connect(partial(close_dialog, self.dlg_catalog)) self.dlg_catalog.btn_accept.clicked.connect(partial(self.fill_geomcat_id, previous_dialog, widget_name)) main_layout = self.dlg_catalog.widget.findChild(QGridLayout, 'main_layout') result = complet_list[0]['body']['data'] for field in result['fields']: label = QLabel() label.setObjectName('lbl_' + field['label']) label.setText(field['label'].capitalize()) widget = None if field['widgettype'] == 'combo': widget = self.add_combobox(self.dlg_catalog, field) if field['layoutname'] == 'lyt_data_1': self.filter_form.addWidget(label, field['layoutorder'], 0) self.filter_form.addWidget(widget, field['layoutorder'], 1) groupBox_1.setLayout(self.filter_form) main_layout.addWidget(groupBox_1) verticalSpacer1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) main_layout.addItem(verticalSpacer1) matcat_id = self.dlg_catalog.findChild(QComboBox, 'matcat_id') pnom = None dnom = None if self.controller.get_project_type() == 'ws': pnom = self.dlg_catalog.findChild(QComboBox, 'pnom') dnom = self.dlg_catalog.findChild(QComboBox, 'dnom') elif self.controller.get_project_type() == 'ud': pnom = self.dlg_catalog.findChild(QComboBox, 'shape') dnom = self.dlg_catalog.findChild(QComboBox, 'geom1') id = self.dlg_catalog.findChild(QComboBox, 'id') # Call get_api_catalog first time self.get_api_catalog(matcat_id, pnom, dnom, id, feature_type, geom_type) # Set Listeners matcat_id.currentIndexChanged.connect( partial(self.populate_pn_dn, matcat_id, pnom, dnom, feature_type, geom_type)) pnom.currentIndexChanged.connect(partial(self.get_api_catalog, matcat_id, pnom, dnom, id, feature_type, geom_type)) dnom.currentIndexChanged.connect(partial(self.get_api_catalog, matcat_id, pnom, dnom, id, feature_type, geom_type)) # Open form open_dialog(self.dlg_catalog, dlg_name='info_catalog') def get_api_catalog(self, matcat_id, pnom, dnom, id, feature_type, geom_type): matcat_id_value = qt_tools.get_item_data(self.dlg_catalog, matcat_id) pn_value = qt_tools.get_item_data(self.dlg_catalog, pnom) dn_value = qt_tools.get_item_data(self.dlg_catalog, dnom) form_name = 'upsert_catalog_' + geom_type + '' form = f'"formName":"{form_name}", "tabName":"data", "editable":"TRUE"' feature = f'"feature_type":"{feature_type}"' extras = None if self.controller.get_project_type() == 'ws': extras = f'"fields":{{"matcat_id":"{matcat_id_value}", "pnom":"{pn_value}", "dnom":"{dn_value}"}}' elif self.controller.get_project_type() == 'ud': extras = f'"fields":{{"matcat_id":"{matcat_id_value}", "shape":"{pn_value}", "geom1":"{dn_value}"}}' body = create_body(form=form, feature=feature, extras=extras) sql = f"SELECT gw_fct_getcatalog({body})::text" row = self.controller.get_row(sql) complet_result = [json.loads(row[0], object_pairs_hook=OrderedDict)] if complet_result[0]['status'] == "Failed": self.controller.log_warning(complet_result[0]) return False if complet_result[0]['status'] == "Accepted": result = complet_result[0]['body']['data'] for field in result['fields']: if field['columnname'] == 'id': self.populate_combo(id, field) def populate_pn_dn(self, matcat_id, pnom, dnom, feature_type, geom_type): matcat_id_value = qt_tools.get_item_data(self.dlg_catalog, matcat_id) form_name = f'upsert_catalog_' + geom_type + '' form = f'"formName":"{form_name}", "tabName":"data", "editable":"TRUE"' feature = f'"feature_type":"{feature_type}"' extras = f'"fields":{{"matcat_id":"{matcat_id_value}"}}' body = create_body(form=form, feature=feature, extras=extras) sql = f"SELECT gw_fct_getcatalog({body})::text" row = self.controller.get_row(sql) complet_list = [json.loads(row[0], object_pairs_hook=OrderedDict)] result = complet_list[0]['body']['data'] for field in result['fields']: if field['columnname'] == 'pnom': self.populate_combo(pnom, field) elif field['columnname'] == 'dnom': self.populate_combo(dnom, field) elif field['columnname'] == 'shape': self.populate_combo(pnom, field) elif field['columnname'] == 'geom1': self.populate_combo(dnom, field) def get_event_combo_parent(self, fields, row, geom_type): if fields == 'fields': for field in row["fields"]: if field['isparent'] is True: widget = self.dlg_catalog.findChild(QComboBox, field['columnname']) widget.currentIndexChanged.connect(partial(self.fill_child, widget, geom_type)) widget.currentIndexChanged.connect(partial(self.populate_catalog_id, geom_type)) def fill_child(self, widget, geom_type): combo_parent = widget.objectName() combo_id = qt_tools.get_item_data(self.dlg_catalog, widget) # TODO cambiar por gw_fct_getchilds sql = f"SELECT gw_fct_getcombochilds('catalog' ,'' ,'' ,'{combo_parent}', '{combo_id}','{geom_type}')" row = self.controller.get_row(sql) for combo_child in row[0]['fields']: if combo_child is not None: self.populate_child(combo_child, row) def populate_catalog_id(self, geom_type): # Get widgets widget_metcat_id = self.dlg_catalog.findChild(QComboBox, 'matcat_id') widget_pn = self.dlg_catalog.findChild(QComboBox, 'pnom') widget_dn = self.dlg_catalog.findChild(QComboBox, 'dnom') widget_id = self.dlg_catalog.findChild(QComboBox, 'id') # Get values from combo parents metcat_value = qt_tools.getWidgetText(self.dlg_catalog, widget_metcat_id) pn_value = qt_tools.getWidgetText(self.dlg_catalog, widget_pn) dn_value = qt_tools.getWidgetText(self.dlg_catalog, widget_dn) exists = self.controller.check_function('gw_api_get_catalog_id') if exists: sql = f"SELECT gw_api_get_catalog_id('{metcat_value}', '{pn_value}', '{dn_value}', '{geom_type}', 9)" row = self.controller.get_row(sql) self.populate_combo(widget_id, row[0]['catalog_id'][0]) def populate_child(self, combo_child, result): child = self.dlg_catalog.findChild(QComboBox, str(combo_child['widgetname'])) if child: self.populate_combo(child, combo_child) def add_combobox(self, dialog, field): widget = QComboBox() widget.setObjectName(field['columnname']) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.populate_combo(widget, field) if 'selectedId' in field: qt_tools.set_combo_itemData(widget, field['selectedId'], 0) return widget def populate_combo(self, widget, field): # Generate list of items to add into combo widget.blockSignals(True) widget.clear() widget.blockSignals(False) combolist = [] if 'comboIds' in field: for i in range(0, len(field['comboIds'])): if field['comboIds'][i] is not None and field['comboNames'][i] is not None: elem = [field['comboIds'][i], field['comboNames'][i]] combolist.append(elem) records_sorted = sorted(combolist, key=operator.itemgetter(1)) # Populate combo if widget.objectName() != 'id': records_sorted.insert(0, ['', '']) for record in records_sorted: widget.addItem(str(record[1]), record) def fill_geomcat_id(self, previous_dialog, widget_name): widget_id = self.dlg_catalog.findChild(QComboBox, 'id') catalog_id = qt_tools.getWidgetText(self.dlg_catalog, widget_id) widget = previous_dialog.findChild(QWidget, widget_name) if type(widget) is QLineEdit: widget.setText(catalog_id) widget.setFocus() elif type(widget) is QComboBox: qt_tools.setWidgetText(previous_dialog, widget, catalog_id) widget.setFocus() else: message = "Widget not found" self.controller.show_message(message, 2, parameter=str(widget_name)) close_dialog(self.dlg_catalog)
def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr('DMS Point Tool')) self.lat_D = QLineEdit() self.lat_M = QLineEdit() self.lat_S = QLineEdit() self.lat_DM = QLineEdit() self.lon_D = QLineEdit() self.lon_M = QLineEdit() self.lon_S = QLineEdit() self.lon_DM = QLineEdit() self.lat_M.textEdited.connect(self.lat_MS_edited) self.lat_S.textEdited.connect(self.lat_MS_edited) self.lat_DM.textEdited.connect(self.lat_DM_edited) self.lon_M.textEdited.connect(self.lon_MS_edited) self.lon_S.textEdited.connect(self.lon_MS_edited) self.lon_DM.textEdited.connect(self.lon_DM_edited) int_val = QIntValidator() int_val.setBottom(0) float_val = QDoubleValidator() float_val.setBottom(0) self.lat_D.setValidator(int_val) self.lat_M.setValidator(int_val) self.lat_S.setValidator(float_val) self.lat_DM.setValidator(float_val) self.lon_D.setValidator(int_val) self.lon_M.setValidator(int_val) self.lon_S.setValidator(float_val) self.lon_DM.setValidator(float_val) self.lat_NS = QComboBox() self.lat_NS.addItem("N") self.lat_NS.addItem("S") self.lon_EW = QComboBox() self.lon_EW.addItem("E") self.lon_EW.addItem("W") buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) lat_grp = QGroupBox(tr("Latitude"), self) lat_grp.setStyleSheet( "QGroupBox { font-weight: bold; color: #3c3c3c; } ") lat_grid = QGridLayout() lat_grid.addWidget(QLabel(tr("Degrees")), 0, 0) lat_grid.addWidget(QLabel(tr("Minutes")), 0, 1) lat_grid.addWidget(QLabel(tr("Seconds")), 0, 2) lat_grid.addWidget(QLabel(tr("Direction")), 0, 3) lat_grid.addWidget(self.lat_D, 1, 0) lat_grid.addWidget(self.lat_M, 1, 1) lat_grid.addWidget(self.lat_S, 1, 2) lat_grid.addWidget(self.lat_NS, 1, 3) lat_grid.addWidget(QLabel(tr("Decimal minutes")), 2, 1) lat_grid.addWidget(self.lat_DM, 3, 1, 1, 2) lat_grp.setLayout(lat_grid) lon_grp = QGroupBox(tr("Longitude"), self) lon_grp.setStyleSheet( "QGroupBox { font-weight: bold; color: #3c3c3c; } ") lon_grid = QGridLayout() lon_grid.addWidget(QLabel(tr("Degrees")), 0, 0) lon_grid.addWidget(QLabel(tr("Minutes")), 0, 1) lon_grid.addWidget(QLabel(tr("Seconds")), 0, 2) lon_grid.addWidget(QLabel(tr("Direction")), 0, 3) lon_grid.addWidget(self.lon_D, 1, 0) lon_grid.addWidget(self.lon_M, 1, 1) lon_grid.addWidget(self.lon_S, 1, 2) lon_grid.addWidget(self.lon_EW, 1, 3) lon_grid.addWidget(QLabel(tr("Decimal minutes")), 2, 1) lon_grid.addWidget(self.lon_DM, 3, 1, 1, 2) lon_grp.setLayout(lon_grid) vbox = QVBoxLayout() vbox.addWidget(lat_grp) vbox.addWidget(lon_grp) vbox.addWidget(buttons) self.setLayout(vbox)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info") ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options..."), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr('DMS Point Tool')) self.lat_D = QLineEdit() self.lat_M = QLineEdit() self.lat_S = QLineEdit() self.lat_DM = QLineEdit() self.lon_D = QLineEdit() self.lon_M = QLineEdit() self.lon_S = QLineEdit() self.lon_DM = QLineEdit() self.lat_M.textEdited.connect(self.lat_MS_edited) self.lat_S.textEdited.connect(self.lat_MS_edited) self.lat_DM.textEdited.connect(self.lat_DM_edited) self.lon_M.textEdited.connect(self.lon_MS_edited) self.lon_S.textEdited.connect(self.lon_MS_edited) self.lon_DM.textEdited.connect(self.lon_DM_edited) int_val = QIntValidator() int_val.setBottom(0) float_val = QDoubleValidator() float_val.setBottom(0) self.lat_D.setValidator(int_val) self.lat_M.setValidator(int_val) self.lat_S.setValidator(float_val) self.lat_DM.setValidator(float_val) self.lon_D.setValidator(int_val) self.lon_M.setValidator(int_val) self.lon_S.setValidator(float_val) self.lon_DM.setValidator(float_val) self.lat_NS = QComboBox() self.lat_NS.addItem("N") self.lat_NS.addItem("S") self.lon_EW = QComboBox() self.lon_EW.addItem("E") self.lon_EW.addItem("W") buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) lat_grp = QGroupBox(tr("Latitude"), self) lat_grp.setStyleSheet( "QGroupBox { font-weight: bold; color: #3c3c3c; } ") lat_grid = QGridLayout() lat_grid.addWidget(QLabel(tr("Degrees")), 0, 0) lat_grid.addWidget(QLabel(tr("Minutes")), 0, 1) lat_grid.addWidget(QLabel(tr("Seconds")), 0, 2) lat_grid.addWidget(QLabel(tr("Direction")), 0, 3) lat_grid.addWidget(self.lat_D, 1, 0) lat_grid.addWidget(self.lat_M, 1, 1) lat_grid.addWidget(self.lat_S, 1, 2) lat_grid.addWidget(self.lat_NS, 1, 3) lat_grid.addWidget(QLabel(tr("Decimal minutes")), 2, 1) lat_grid.addWidget(self.lat_DM, 3, 1, 1, 2) lat_grp.setLayout(lat_grid) lon_grp = QGroupBox(tr("Longitude"), self) lon_grp.setStyleSheet( "QGroupBox { font-weight: bold; color: #3c3c3c; } ") lon_grid = QGridLayout() lon_grid.addWidget(QLabel(tr("Degrees")), 0, 0) lon_grid.addWidget(QLabel(tr("Minutes")), 0, 1) lon_grid.addWidget(QLabel(tr("Seconds")), 0, 2) lon_grid.addWidget(QLabel(tr("Direction")), 0, 3) lon_grid.addWidget(self.lon_D, 1, 0) lon_grid.addWidget(self.lon_M, 1, 1) lon_grid.addWidget(self.lon_S, 1, 2) lon_grid.addWidget(self.lon_EW, 1, 3) lon_grid.addWidget(QLabel(tr("Decimal minutes")), 2, 1) lon_grid.addWidget(self.lon_DM, 3, 1, 1, 2) lon_grp.setLayout(lon_grid) vbox = QVBoxLayout() vbox.addWidget(lat_grp) vbox.addWidget(lon_grp) vbox.addWidget(buttons) self.setLayout(vbox)
def setup_left_panel(self): """Setup the UI for left panel. Generate all exposure, combobox, and edit button. """ hazard = self.parent.step_kw_subcategory.selected_subcategory() left_panel_heading = QLabel(tr('Classifications')) left_panel_heading.setFont(big_font) self.left_layout.addWidget(left_panel_heading) inner_left_layout = QGridLayout() row = 0 for exposure in exposure_all: special_case = False if not setting('developer_mode'): # Filter out unsupported exposure for the hazard if exposure in hazard['disabled_exposures']: # Remove from the storage if the exposure is disabled if self.layer_mode == layer_mode_continuous: if exposure['key'] in self.thresholds: self.thresholds.pop(exposure['key']) else: if exposure['key'] in self.value_maps: self.value_maps.pop(exposure['key']) continue # Trick for EQ raster for population #3853 if exposure == exposure_population and hazard == hazard_earthquake: if is_raster_layer(self.parent.layer): if self.layer_mode == layer_mode_continuous: self.use_default_thresholds = True special_case = True # Set classification for EQ Raster for Population self.thresholds[exposure_population['key']] = { earthquake_mmi_scale['key']: { 'classes': default_classification_thresholds( earthquake_mmi_scale), 'active': True } } # Add label # Hazard on Exposure Classifications label = tr( '{hazard_name} on {exposure_name} Classifications').format( hazard_name=hazard['name'], exposure_name=exposure['name'] ) exposure_label = QLabel(label) # Add combo box exposure_combo_box = QComboBox() hazard_classifications = hazard.get('classifications') exposure_combo_box.addItem(tr('No classifications')) exposure_combo_box.setItemData( 0, None, Qt.UserRole) current_index = 0 i = 0 # Iterate through all available hazard classifications for hazard_classification in hazard_classifications: # Skip if the classification is not for the exposure if 'exposures' in hazard_classification: if exposure not in hazard_classification['exposures']: continue exposure_combo_box.addItem(hazard_classification['name']) exposure_combo_box.setItemData( i + 1, hazard_classification, Qt.UserRole) if self.layer_mode == layer_mode_continuous: current_hazard_classifications = self.thresholds.get( exposure['key']) else: current_hazard_classifications = self.value_maps.get( exposure['key']) if current_hazard_classifications: current_hazard_classification = \ current_hazard_classifications.get( hazard_classification['key']) if current_hazard_classification: is_active = current_hazard_classification.get('active') if is_active: current_index = i + 1 i += 1 # Set current classification exposure_combo_box.setCurrentIndex(current_index) # Add edit button exposure_edit_button = QPushButton(tr('Edit')) # For special case. Raster EQ on Population. if special_case: mmi_index = exposure_combo_box.findText( earthquake_mmi_scale['name']) exposure_combo_box.setCurrentIndex(mmi_index) exposure_combo_box.setEnabled(False) exposure_edit_button.setEnabled(False) tool_tip_message = tr( 'InaSAFE use default classification for Raster Earthquake ' 'hazard on population.') exposure_label.setToolTip(tool_tip_message) exposure_combo_box.setToolTip(tool_tip_message) exposure_edit_button.setToolTip(tool_tip_message) else: if current_index == 0: # Disable if there is no classification chosen. exposure_edit_button.setEnabled(False) exposure_edit_button.clicked.connect( partial( self.edit_button_clicked, edit_button=exposure_edit_button, exposure_combo_box=exposure_combo_box, exposure=exposure)) exposure_combo_box.currentIndexChanged.connect( partial( self.classifications_combo_box_changed, exposure=exposure, exposure_combo_box=exposure_combo_box, edit_button=exposure_edit_button)) # Arrange in layout inner_left_layout.addWidget(exposure_label, row, 0) inner_left_layout.addWidget(exposure_combo_box, row, 1) inner_left_layout.addWidget(exposure_edit_button, row, 2) # Adding to step's attribute self.exposures.append(exposure) self.exposure_combo_boxes.append(exposure_combo_box) self.exposure_edit_buttons.append(exposure_edit_button) self.exposure_labels.append(label) if special_case: self.special_case_index = len(self.exposures) - 1 row += 1 self.left_layout.addLayout(inner_left_layout) # To push the inner_left_layout up self.left_layout.addStretch(1)
class PythonConsoleWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console")) self.settings = QgsSettings() self.shell = ShellScintilla(self) self.setFocusProxy(self.shell) self.shellOut = ShellOutputScintilla(self) self.tabEditorWidget = EditorTabWidget(self) # ------------ UI ------------------------------- self.splitterEditor = QSplitter(self) self.splitterEditor.setOrientation(Qt.Horizontal) self.splitterEditor.setHandleWidth(6) self.splitterEditor.setChildrenCollapsible(True) self.shellOutWidget = QWidget(self) self.shellOutWidget.setLayout(QVBoxLayout()) self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0) self.shellOutWidget.layout().addWidget(self.shellOut) self.splitter = QSplitter(self.splitterEditor) self.splitter.setOrientation(Qt.Vertical) self.splitter.setHandleWidth(3) self.splitter.setChildrenCollapsible(False) self.splitter.addWidget(self.shellOutWidget) self.splitter.addWidget(self.shell) # self.splitterEditor.addWidget(self.tabEditorWidget) self.splitterObj = QSplitter(self.splitterEditor) self.splitterObj.setHandleWidth(3) self.splitterObj.setOrientation(Qt.Horizontal) # self.splitterObj.setSizes([0, 0]) # self.splitterObj.setStretchFactor(0, 1) self.widgetEditor = QWidget(self.splitterObj) self.widgetFind = QWidget(self) self.listClassMethod = QTreeWidget(self.splitterObj) self.listClassMethod.setColumnCount(2) objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector") self.listClassMethod.setHeaderLabels([objInspLabel, '']) self.listClassMethod.setColumnHidden(1, True) self.listClassMethod.setAlternatingRowColors(True) # self.splitterEditor.addWidget(self.widgetEditor) # self.splitterObj.addWidget(self.listClassMethod) # self.splitterObj.addWidget(self.widgetEditor) # Hide side editor on start up self.splitterObj.hide() self.listClassMethod.hide() # Hide search widget on start up self.widgetFind.hide() icon_size = iface.iconSize(dockedToolbar=True) if iface else QSize(16, 16) sizes = self.splitter.sizes() self.splitter.setSizes(sizes) # ----------------Restore Settings------------------------------------ self.restoreSettingsConsole() # ------------------Toolbar Editor------------------------------------- # Action for Open File openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…") self.openFileButton = QAction(self) self.openFileButton.setCheckable(False) self.openFileButton.setEnabled(True) self.openFileButton.setIcon(QgsApplication.getThemeIcon("console/iconOpenConsole.svg")) self.openFileButton.setMenuRole(QAction.PreferencesRole) self.openFileButton.setIconVisibleInMenu(True) self.openFileButton.setToolTip(openFileBt) self.openFileButton.setText(openFileBt) openExtEditorBt = QCoreApplication.translate("PythonConsole", "Open in External Editor") self.openInEditorButton = QAction(self) self.openInEditorButton.setCheckable(False) self.openInEditorButton.setEnabled(True) self.openInEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")) self.openInEditorButton.setMenuRole(QAction.PreferencesRole) self.openInEditorButton.setIconVisibleInMenu(True) self.openInEditorButton.setToolTip(openExtEditorBt) self.openInEditorButton.setText(openExtEditorBt) # Action for Save File saveFileBt = QCoreApplication.translate("PythonConsole", "Save") self.saveFileButton = QAction(self) self.saveFileButton.setCheckable(False) self.saveFileButton.setEnabled(False) self.saveFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveConsole.svg")) self.saveFileButton.setMenuRole(QAction.PreferencesRole) self.saveFileButton.setIconVisibleInMenu(True) self.saveFileButton.setToolTip(saveFileBt) self.saveFileButton.setText(saveFileBt) # Action for Save File As saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…") self.saveAsFileButton = QAction(self) self.saveAsFileButton.setCheckable(False) self.saveAsFileButton.setEnabled(True) self.saveAsFileButton.setIcon(QgsApplication.getThemeIcon("console/iconSaveAsConsole.svg")) self.saveAsFileButton.setMenuRole(QAction.PreferencesRole) self.saveAsFileButton.setIconVisibleInMenu(True) self.saveAsFileButton.setToolTip(saveAsFileBt) self.saveAsFileButton.setText(saveAsFileBt) # Action Cut cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") self.cutEditorButton = QAction(self) self.cutEditorButton.setCheckable(False) self.cutEditorButton.setEnabled(True) self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cutEditorButton.setMenuRole(QAction.PreferencesRole) self.cutEditorButton.setIconVisibleInMenu(True) self.cutEditorButton.setToolTip(cutEditorBt) self.cutEditorButton.setText(cutEditorBt) # Action Copy copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") self.copyEditorButton = QAction(self) self.copyEditorButton.setCheckable(False) self.copyEditorButton.setEnabled(True) self.copyEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg")) self.copyEditorButton.setMenuRole(QAction.PreferencesRole) self.copyEditorButton.setIconVisibleInMenu(True) self.copyEditorButton.setToolTip(copyEditorBt) self.copyEditorButton.setText(copyEditorBt) # Action Paste pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") self.pasteEditorButton = QAction(self) self.pasteEditorButton.setCheckable(False) self.pasteEditorButton.setEnabled(True) self.pasteEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg")) self.pasteEditorButton.setMenuRole(QAction.PreferencesRole) self.pasteEditorButton.setIconVisibleInMenu(True) self.pasteEditorButton.setToolTip(pasteEditorBt) self.pasteEditorButton.setText(pasteEditorBt) # Action Run Script (subprocess) runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script") self.runScriptEditorButton = QAction(self) self.runScriptEditorButton.setCheckable(False) self.runScriptEditorButton.setEnabled(True) self.runScriptEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconRunScriptConsole.svg")) self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole) self.runScriptEditorButton.setIconVisibleInMenu(True) self.runScriptEditorButton.setToolTip(runScriptEditorBt) self.runScriptEditorButton.setText(runScriptEditorBt) # Action Run Script (subprocess) commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment") self.commentEditorButton = QAction(self) self.commentEditorButton.setCheckable(False) self.commentEditorButton.setEnabled(True) self.commentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg")) self.commentEditorButton.setMenuRole(QAction.PreferencesRole) self.commentEditorButton.setIconVisibleInMenu(True) self.commentEditorButton.setToolTip(commentEditorBt) self.commentEditorButton.setText(commentEditorBt) # Action Run Script (subprocess) uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment") self.uncommentEditorButton = QAction(self) self.uncommentEditorButton.setCheckable(False) self.uncommentEditorButton.setEnabled(True) self.uncommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.svg")) self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole) self.uncommentEditorButton.setIconVisibleInMenu(True) self.uncommentEditorButton.setToolTip(uncommentEditorBt) self.uncommentEditorButton.setText(uncommentEditorBt) # Action for Object browser objList = QCoreApplication.translate("PythonConsole", "Object Inspector…") self.objectListButton = QAction(self) self.objectListButton.setCheckable(True) self.objectListButton.setEnabled(self.settings.value("pythonConsole/enableObjectInsp", False, type=bool)) self.objectListButton.setIcon(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")) self.objectListButton.setMenuRole(QAction.PreferencesRole) self.objectListButton.setIconVisibleInMenu(True) self.objectListButton.setToolTip(objList) self.objectListButton.setText(objList) # Action for Find text findText = QCoreApplication.translate("PythonConsole", "Find Text") self.findTextButton = QAction(self) self.findTextButton.setCheckable(True) self.findTextButton.setEnabled(True) self.findTextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg")) self.findTextButton.setMenuRole(QAction.PreferencesRole) self.findTextButton.setIconVisibleInMenu(True) self.findTextButton.setToolTip(findText) self.findTextButton.setText(findText) # ----------------Toolbar Console------------------------------------- # Action Show Editor showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") self.showEditorButton = QAction(self) self.showEditorButton.setEnabled(True) self.showEditorButton.setCheckable(True) self.showEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")) self.showEditorButton.setMenuRole(QAction.PreferencesRole) self.showEditorButton.setIconVisibleInMenu(True) self.showEditorButton.setToolTip(showEditor) self.showEditorButton.setText(showEditor) # Action for Clear button clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") self.clearButton = QAction(self) self.clearButton.setCheckable(False) self.clearButton.setEnabled(True) self.clearButton.setIcon(QgsApplication.getThemeIcon("console/iconClearConsole.svg")) self.clearButton.setMenuRole(QAction.PreferencesRole) self.clearButton.setIconVisibleInMenu(True) self.clearButton.setToolTip(clearBt) self.clearButton.setText(clearBt) # Action for settings optionsBt = QCoreApplication.translate("PythonConsole", "Options…") self.optionsButton = QAction(self) self.optionsButton.setCheckable(False) self.optionsButton.setEnabled(True) self.optionsButton.setIcon(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")) self.optionsButton.setMenuRole(QAction.PreferencesRole) self.optionsButton.setIconVisibleInMenu(True) self.optionsButton.setToolTip(optionsBt) self.optionsButton.setText(optionsBt) # Action for Run script runBt = QCoreApplication.translate("PythonConsole", "Run Command") self.runButton = QAction(self) self.runButton.setCheckable(False) self.runButton.setEnabled(True) self.runButton.setIcon(QgsApplication.getThemeIcon("console/mIconRunConsole.svg")) self.runButton.setMenuRole(QAction.PreferencesRole) self.runButton.setIconVisibleInMenu(True) self.runButton.setToolTip(runBt) self.runButton.setText(runBt) # Help action helpBt = QCoreApplication.translate("PythonConsole", "Help…") self.helpButton = QAction(self) self.helpButton.setCheckable(False) self.helpButton.setEnabled(True) self.helpButton.setIcon(QgsApplication.getThemeIcon("console/iconHelpConsole.svg")) self.helpButton.setMenuRole(QAction.PreferencesRole) self.helpButton.setIconVisibleInMenu(True) self.helpButton.setToolTip(helpBt) self.helpButton.setText(helpBt) self.toolBar = QToolBar() self.toolBar.setEnabled(True) self.toolBar.setFocusPolicy(Qt.NoFocus) self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBar.setLayoutDirection(Qt.LeftToRight) self.toolBar.setIconSize(icon_size) self.toolBar.setMovable(False) self.toolBar.setFloatable(False) self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.runButton) self.toolBar.addSeparator() self.toolBar.addAction(self.showEditorButton) self.toolBar.addSeparator() self.toolBar.addAction(self.optionsButton) self.toolBar.addAction(self.helpButton) self.toolBarEditor = QToolBar() self.toolBarEditor.setEnabled(False) self.toolBarEditor.setFocusPolicy(Qt.NoFocus) self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBarEditor.setLayoutDirection(Qt.LeftToRight) self.toolBarEditor.setIconSize(icon_size) self.toolBarEditor.setMovable(False) self.toolBarEditor.setFloatable(False) self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.findTextButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.commentEditorButton) self.toolBarEditor.addAction(self.uncommentEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.objectListButton) self.widgetButton = QWidget() sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButton.sizePolicy().hasHeightForWidth()) self.widgetButton.setSizePolicy(sizePolicy) self.widgetButtonEditor = QWidget(self.widgetEditor) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButtonEditor.sizePolicy().hasHeightForWidth()) self.widgetButtonEditor.setSizePolicy(sizePolicy) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.shellOut.sizePolicy().hasHeightForWidth()) self.shellOut.setSizePolicy(sizePolicy) self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # ------------ Layout ------------------------------- self.mainLayout = QGridLayout(self) self.mainLayout.setMargin(0) self.mainLayout.setSpacing(0) self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1) self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1) self.shellOutWidget.layout().insertWidget(0, self.toolBar) self.layoutEditor = QGridLayout(self.widgetEditor) self.layoutEditor.setMargin(0) self.layoutEditor.setSpacing(0) self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1) self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1) self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1) self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1) # Layout for the find widget self.layoutFind = QGridLayout(self.widgetFind) self.layoutFind.setContentsMargins(0, 0, 0, 0) self.lineEditFind = QgsFilterLineEdit() placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find…") self.lineEditFind.setPlaceholderText(placeHolderTxt) self.toolBarFindText = QToolBar() self.toolBarFindText.setIconSize(icon_size) self.findNextButton = QAction(self) self.findNextButton.setEnabled(False) toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next") self.findNextButton.setToolTip(toolTipfindNext) self.findNextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchNextEditorConsole.svg")) self.findPrevButton = QAction(self) self.findPrevButton.setEnabled(False) toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous") self.findPrevButton.setToolTip(toolTipfindPrev) self.findPrevButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchPrevEditorConsole.svg")) self.caseSensitive = QCheckBox() caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive") self.caseSensitive.setText(caseSensTr) self.wholeWord = QCheckBox() wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word") self.wholeWord.setText(wholeWordTr) self.wrapAround = QCheckBox() self.wrapAround.setChecked(True) wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around") self.wrapAround.setText(wrapAroundTr) self.toolBarFindText.addWidget(self.lineEditFind) self.toolBarFindText.addAction(self.findPrevButton) self.toolBarFindText.addAction(self.findNextButton) self.toolBarFindText.addWidget(self.caseSensitive) self.toolBarFindText.addWidget(self.wholeWord) self.toolBarFindText.addWidget(self.wrapAround) self.layoutFind.addWidget(self.toolBarFindText, 0, 1, 1, 1) # ------------ Add first Tab in Editor ------------------------------- # self.tabEditorWidget.newTabEditor(tabName='first', filename=None) # ------------ Signal ------------------------------- self.findTextButton.triggered.connect(self._toggleFind) self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.commentEditorButton.triggered.connect(self.commentCode) self.uncommentEditorButton.triggered.connect(self.uncommentCode) self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.cutEditorButton.triggered.connect(self.cutEditor) self.copyEditorButton.triggered.connect(self.copyEditor) self.pasteEditorButton.triggered.connect(self.pasteEditor) self.showEditorButton.toggled.connect(self.toggleEditor) self.clearButton.triggered.connect(self.shellOut.clearConsole) self.optionsButton.triggered.connect(self.openSettings) self.runButton.triggered.connect(self.shell.entered) self.openFileButton.triggered.connect(self.openScriptFile) self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.saveFileButton.triggered.connect(self.saveScriptFile) self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.helpButton.triggered.connect(self.openHelp) self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.lineEditFind.returnPressed.connect(self._findNext) self.findNextButton.triggered.connect(self._findNext) self.findPrevButton.triggered.connect(self._findPrev) self.lineEditFind.textChanged.connect(self._textFindChanged) self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._openFind) self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor) self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut) self.findNextScut.activated.connect(self._findNext) self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor) self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut) self.findPreviousScut.activated.connect(self._findPrev) # Escape on editor hides the find bar self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._closeFind) def _toggleFind(self): self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget() def _openFind(self): self.tabEditorWidget.currentWidget().newEditor.openFindWidget() def _closeFind(self): self.tabEditorWidget.currentWidget().newEditor.closeFindWidget() def _findNext(self): self.tabEditorWidget.currentWidget().newEditor.findText(True) def _findPrev(self): self.tabEditorWidget.currentWidget().newEditor.findText(False) def _textFindChanged(self): if self.lineEditFind.text(): self.findNextButton.setEnabled(True) self.findPrevButton.setEnabled(True) self.tabEditorWidget.currentWidget().newEditor.findText(True, showMessage=False, findFirst=True) else: self.lineEditFind.setStyleSheet('') self.findNextButton.setEnabled(False) self.findPrevButton.setEnabled(False) def onClickGoToLine(self, item, column): tabEditor = self.tabEditorWidget.currentWidget().newEditor if item.text(1) == 'syntaxError': check = tabEditor.syntaxCheck(fromContextMenu=False) if check and not tabEditor.isReadOnly(): self.tabEditorWidget.currentWidget().save() return linenr = int(item.text(1)) itemName = str(item.text(0)) charPos = itemName.find(' ') if charPos != -1: objName = itemName[0:charPos] else: objName = itemName tabEditor.goToLine(objName, linenr) def toggleEditor(self, checked): self.splitterObj.show() if checked else self.splitterObj.hide() if not self.tabEditorWidget: self.tabEditorWidget.enableToolBarEditor(checked) self.tabEditorWidget.restoreTabsOrAddNew() def toggleObjectListWidget(self, checked): self.listClassMethod.show() if checked else self.listClassMethod.hide() def pasteEditor(self): self.tabEditorWidget.currentWidget().newEditor.paste() def cutEditor(self): self.tabEditorWidget.currentWidget().newEditor.cut() def copyEditor(self): self.tabEditorWidget.currentWidget().newEditor.copy() def runScriptEditor(self): self.tabEditorWidget.currentWidget().newEditor.runScriptCode() def commentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True) def uncommentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False) def openScriptFileExtEditor(self): tabWidget = self.tabEditorWidget.currentWidget() path = tabWidget.path import subprocess try: subprocess.Popen([os.environ['EDITOR'], path]) except KeyError: QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def openScriptFile(self): lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) openFileTr = QCoreApplication.translate("PythonConsole", "Open File") fileList, selected_filter = QFileDialog.getOpenFileNames( self, openFileTr, lastDirPath, "Script file (*.py)") if fileList: for pyFile in fileList: for i in range(self.tabEditorWidget.count()): tabWidget = self.tabEditorWidget.widget(i) if tabWidget.path == pyFile: self.tabEditorWidget.setCurrentWidget(tabWidget) break else: tabName = QFileInfo(pyFile).fileName() self.tabEditorWidget.newTabEditor(tabName, pyFile) lastDirPath = QFileInfo(pyFile).path() self.settings.setValue("pythonConsole/lastDirPath", pyFile) self.updateTabListScript(pyFile, action='append') def saveScriptFile(self): tabWidget = self.tabEditorWidget.currentWidget() try: tabWidget.save() except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) def saveAsScriptFile(self, index=None): tabWidget = self.tabEditorWidget.currentWidget() if not index: index = self.tabEditorWidget.currentIndex() if not tabWidget.path: fileName = self.tabEditorWidget.tabText(index) + '.py' folder = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) pathFileName = os.path.join(folder, fileName) fileNone = True else: pathFileName = tabWidget.path fileNone = False saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As") filename, filter = QFileDialog.getSaveFileName(self, saveAsFileTr, pathFileName, "Script file (*.py)") if filename: try: tabWidget.save(filename) except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) if fileNone: tabWidget.path = None else: tabWidget.path = pathFileName return if not fileNone: self.updateTabListScript(pathFileName, action='remove') def openHelp(self): QgsHelp.openHelp("plugins/python_console.html") def openSettings(self): if optionsDialog(self).exec_(): self.shell.refreshSettingsShell() self.shellOut.refreshSettingsOutput() self.tabEditorWidget.refreshSettingsEditor() def callWidgetMessageBar(self, text): self.shellOut.widgetMessageBar(iface, text) def callWidgetMessageBarEditor(self, text, level, timed): self.tabEditorWidget.widgetMessageBar(iface, text, level, timed) def updateTabListScript(self, script, action=None): if action == 'remove': self.tabListScript.remove(script) elif action == 'append': if not self.tabListScript: self.tabListScript = [] if script not in self.tabListScript: self.tabListScript.append(script) else: self.tabListScript = [] self.settings.setValue("pythonConsole/tabScripts", self.tabListScript) def saveSettingsConsole(self): self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState()) self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState()) self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState()) self.shell.writeHistoryFile(True) def restoreSettingsConsole(self): storedTabScripts = self.settings.value("pythonConsole/tabScripts", []) self.tabListScript = storedTabScripts self.splitter.restoreState(self.settings.value("pythonConsole/splitterConsole", QByteArray())) self.splitterEditor.restoreState(self.settings.value("pythonConsole/splitterEditor", QByteArray())) self.splitterObj.restoreState(self.settings.value("pythonConsole/splitterObj", QByteArray()))
def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr('Rectangle size')) self.width = QLineEdit() self.height = QLineEdit() width_val = QDoubleValidator() height_val = QDoubleValidator() self.width.setValidator(width_val) self.height.setValidator(height_val) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) grid = QGridLayout() grid.addWidget(QLabel(tr('Give a size in m:')), 0, 0) grid.addWidget(QLabel(tr('Width:')), 1, 0) grid.addWidget(QLabel(tr('Height:')), 1, 1) grid.addWidget(self.width, 2, 0) grid.addWidget(self.height, 2, 1) grid.addWidget(buttons, 3, 0, 1, 2) self.setLayout(grid)
def main(): """Main function to run the example.""" from safe.test.utilities import get_qgis_app QGIS_APP, CANVAS, IFACE, PARENT = get_qgis_app(qsetting=INASAFE_TEST) options = OrderedDict([ (DO_NOT_REPORT, { 'label': 'Do not report', 'value': None, 'type': STATIC, 'constraint': {} }), (GLOBAL_DEFAULT, { 'label': 'Global default', 'value': 0.5, 'type': STATIC, 'constraint': {} }), (CUSTOM_VALUE, { 'label': 'Custom', 'value': 0.7, # Taken from keywords / recent value 'type': SINGLE_DYNAMIC, 'constraint': { 'min': 0, 'max': 1 } }), (FIELDS, { 'label': 'Ratio fields', 'value': ['field A', 'field B', 'field C'], # Taken from keywords 'type': MULTIPLE_DYNAMIC, 'constraint': {} }) ]) default_value_parameter = GroupSelectParameter() default_value_parameter.name = 'Group Select parameter' default_value_parameter.help_text = 'Help text' default_value_parameter.description = 'Description' default_value_parameter.options = options default_value_parameter.selected = 'ratio fields' parameters = [ default_value_parameter ] extra_parameters = [ (GroupSelectParameter, GroupSelectParameterWidget) ] parameter_container = ParameterContainer( parameters, extra_parameters=extra_parameters) parameter_container.setup_ui() widget = QWidget() layout = QGridLayout() layout.addWidget(parameter_container) def show_error_message(parent, exception): """Generate error message to handle parameter errors :param parent: The widget as a parent of message box :type parent: QWidget :param exception: python Exception or Error :type exception: Exception """ box = QMessageBox() box.critical(parent, 'Error occured', str(exception)) def show_parameter(the_parameter_container): """Print the value of parameter. :param the_parameter_container: A parameter container :type the_parameter_container: ParameterContainer """ def show_parameter_value(a_parameter): if isinstance(a_parameter, GroupParameter): for param in a_parameter.value: show_parameter_value(param) else: print((a_parameter.guid, a_parameter.name, a_parameter.value)) try: the_parameters = the_parameter_container.get_parameters() if the_parameters: for parameter in the_parameters: show_parameter_value(parameter) except Exception as inst: show_error_message(parameter_container, inst) print_button = QPushButton('Print Result') # noinspection PyUnresolvedReferences print_button.clicked.connect( partial(show_parameter, the_parameter_container=parameter_container)) layout.addWidget(print_button) widget.setLayout(layout) widget.setGeometry(0, 0, 500, 500) widget.show() sys.exit(QGIS_APP.exec_())
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 DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QgsSettings() self.restoreGeometry(settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState(settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.tabs.currentChanged.connect(self.tabChanged) self.tree.selectedItemChanged.connect(self.itemChanged) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) # save the window state settings = QgsSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): with OverrideCursor(Qt.WaitCursor): try: if item is None: item = self.tree.currentItem() self.tree.refreshItem(item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) def itemChanged(self, item): with OverrideCursor(Qt.WaitCursor): try: self.reloadButtons() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): with OverrideCursor(Qt.WaitCursor): try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled(self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage(self.tr("Select the table you want export to file."), QgsMessageBar.INFO, self.iface.messageTimeout()) return inLayer = table.toMapLayer() if inLayer.type() != QgsMapLayer.VectorLayer: self.infoBar.pushMessage( self.tr("Select a vector or a tabular layer you want export."), QgsMessageBar.WARNING, self.iface.messageTimeout()) return from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from .dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query ({0})").format(dbname) index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect(functools.partial(self.update_query_tab_name, index, dbname)) def runSqlLayerWindow(self, layer): from .dlg_sql_layer_window import DlgSqlLayerWindow query = DlgSqlLayerWindow(self.iface, layer, self) lname = layer.name() tabname = self.tr("Layer ({0})").format(lname) index = self.tabs.addTab(query, tabname) # self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: def invoke_callback(x): return self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName().endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ with OverrideCursor(Qt.WaitCursor): try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: self.tabs.removeTab(index) widget.deleteLater() def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(i, QTabBar.RightSide) else tabbar.tabButton(i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget("Tree", self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar("Default", self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction(QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction(QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction(QIcon(":/db_manager/actions/import"), self.tr("&Import layer/file"), self.importActionSlot) self.actionExport = self.menuTable.addAction(QIcon(":/db_manager/actions/export"), self.tr("&Export to file"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
class PythonConsoleWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console")) self.settings = QgsSettings() self.shell = ShellScintilla(self) self.setFocusProxy(self.shell) self.shellOut = ShellOutputScintilla(self) self.tabEditorWidget = EditorTabWidget(self) # ------------ UI ------------------------------- self.splitterEditor = QSplitter(self) self.splitterEditor.setOrientation(Qt.Horizontal) self.splitterEditor.setHandleWidth(6) self.splitterEditor.setChildrenCollapsible(True) self.shellOutWidget = QWidget(self) self.shellOutWidget.setLayout(QVBoxLayout()) self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0) self.shellOutWidget.layout().addWidget(self.shellOut) self.splitter = QSplitter(self.splitterEditor) self.splitter.setOrientation(Qt.Vertical) self.splitter.setHandleWidth(3) self.splitter.setChildrenCollapsible(False) self.splitter.addWidget(self.shellOutWidget) self.splitter.addWidget(self.shell) # self.splitterEditor.addWidget(self.tabEditorWidget) self.splitterObj = QSplitter(self.splitterEditor) self.splitterObj.setHandleWidth(3) self.splitterObj.setOrientation(Qt.Horizontal) # self.splitterObj.setSizes([0, 0]) # self.splitterObj.setStretchFactor(0, 1) self.widgetEditor = QWidget(self.splitterObj) self.widgetFind = QWidget(self) self.listClassMethod = QTreeWidget(self.splitterObj) self.listClassMethod.setColumnCount(2) objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector") self.listClassMethod.setHeaderLabels([objInspLabel, '']) self.listClassMethod.setColumnHidden(1, True) self.listClassMethod.setAlternatingRowColors(True) # self.splitterEditor.addWidget(self.widgetEditor) # self.splitterObj.addWidget(self.listClassMethod) # self.splitterObj.addWidget(self.widgetEditor) # Hide side editor on start up self.splitterObj.hide() self.listClassMethod.hide() # Hide search widget on start up self.widgetFind.hide() icon_size = iface.iconSize(dockedToolbar=True) if iface else QSize(16, 16) sizes = self.splitter.sizes() self.splitter.setSizes(sizes) # ----------------Restore Settings------------------------------------ self.restoreSettingsConsole() # ------------------Toolbar Editor------------------------------------- # Action for Open File openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…") self.openFileButton = QAction(self) self.openFileButton.setCheckable(False) self.openFileButton.setEnabled(True) self.openFileButton.setIcon(QgsApplication.getThemeIcon("mActionScriptOpen.svg")) self.openFileButton.setMenuRole(QAction.PreferencesRole) self.openFileButton.setIconVisibleInMenu(True) self.openFileButton.setToolTip(openFileBt) self.openFileButton.setText(openFileBt) openExtEditorBt = QCoreApplication.translate("PythonConsole", "Open in External Editor") self.openInEditorButton = QAction(self) self.openInEditorButton.setCheckable(False) self.openInEditorButton.setEnabled(True) self.openInEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")) self.openInEditorButton.setMenuRole(QAction.PreferencesRole) self.openInEditorButton.setIconVisibleInMenu(True) self.openInEditorButton.setToolTip(openExtEditorBt) self.openInEditorButton.setText(openExtEditorBt) # Action for Save File saveFileBt = QCoreApplication.translate("PythonConsole", "Save") self.saveFileButton = QAction(self) self.saveFileButton.setCheckable(False) self.saveFileButton.setEnabled(False) self.saveFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSave.svg")) self.saveFileButton.setMenuRole(QAction.PreferencesRole) self.saveFileButton.setIconVisibleInMenu(True) self.saveFileButton.setToolTip(saveFileBt) self.saveFileButton.setText(saveFileBt) # Action for Save File As saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…") self.saveAsFileButton = QAction(self) self.saveAsFileButton.setCheckable(False) self.saveAsFileButton.setEnabled(True) self.saveAsFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSaveAs.svg")) self.saveAsFileButton.setMenuRole(QAction.PreferencesRole) self.saveAsFileButton.setIconVisibleInMenu(True) self.saveAsFileButton.setToolTip(saveAsFileBt) self.saveAsFileButton.setText(saveAsFileBt) # Action Cut cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") self.cutEditorButton = QAction(self) self.cutEditorButton.setCheckable(False) self.cutEditorButton.setEnabled(True) self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cutEditorButton.setMenuRole(QAction.PreferencesRole) self.cutEditorButton.setIconVisibleInMenu(True) self.cutEditorButton.setToolTip(cutEditorBt) self.cutEditorButton.setText(cutEditorBt) # Action Copy copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") self.copyEditorButton = QAction(self) self.copyEditorButton.setCheckable(False) self.copyEditorButton.setEnabled(True) self.copyEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg")) self.copyEditorButton.setMenuRole(QAction.PreferencesRole) self.copyEditorButton.setIconVisibleInMenu(True) self.copyEditorButton.setToolTip(copyEditorBt) self.copyEditorButton.setText(copyEditorBt) # Action Paste pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") self.pasteEditorButton = QAction(self) self.pasteEditorButton.setCheckable(False) self.pasteEditorButton.setEnabled(True) self.pasteEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg")) self.pasteEditorButton.setMenuRole(QAction.PreferencesRole) self.pasteEditorButton.setIconVisibleInMenu(True) self.pasteEditorButton.setToolTip(pasteEditorBt) self.pasteEditorButton.setText(pasteEditorBt) # Action Run Script (subprocess) runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script") self.runScriptEditorButton = QAction(self) self.runScriptEditorButton.setCheckable(False) self.runScriptEditorButton.setEnabled(True) self.runScriptEditorButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg")) self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole) self.runScriptEditorButton.setIconVisibleInMenu(True) self.runScriptEditorButton.setToolTip(runScriptEditorBt) self.runScriptEditorButton.setText(runScriptEditorBt) # Action Run Script (subprocess) commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment") self.commentEditorButton = QAction(self) self.commentEditorButton.setCheckable(False) self.commentEditorButton.setEnabled(True) self.commentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg")) self.commentEditorButton.setMenuRole(QAction.PreferencesRole) self.commentEditorButton.setIconVisibleInMenu(True) self.commentEditorButton.setToolTip(commentEditorBt) self.commentEditorButton.setText(commentEditorBt) # Action Run Script (subprocess) uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment") self.uncommentEditorButton = QAction(self) self.uncommentEditorButton.setCheckable(False) self.uncommentEditorButton.setEnabled(True) self.uncommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.svg")) self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole) self.uncommentEditorButton.setIconVisibleInMenu(True) self.uncommentEditorButton.setToolTip(uncommentEditorBt) self.uncommentEditorButton.setText(uncommentEditorBt) # Action for Object browser objList = QCoreApplication.translate("PythonConsole", "Object Inspector…") self.objectListButton = QAction(self) self.objectListButton.setCheckable(True) self.objectListButton.setEnabled(self.settings.value("pythonConsole/enableObjectInsp", False, type=bool)) self.objectListButton.setIcon(QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")) self.objectListButton.setMenuRole(QAction.PreferencesRole) self.objectListButton.setIconVisibleInMenu(True) self.objectListButton.setToolTip(objList) self.objectListButton.setText(objList) # Action for Find text findText = QCoreApplication.translate("PythonConsole", "Find Text") self.findTextButton = QAction(self) self.findTextButton.setCheckable(True) self.findTextButton.setEnabled(True) self.findTextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchEditorConsole.svg")) self.findTextButton.setMenuRole(QAction.PreferencesRole) self.findTextButton.setIconVisibleInMenu(True) self.findTextButton.setToolTip(findText) self.findTextButton.setText(findText) # ----------------Toolbar Console------------------------------------- # Action Show Editor showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") self.showEditorButton = QAction(self) self.showEditorButton.setEnabled(True) self.showEditorButton.setCheckable(True) self.showEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")) self.showEditorButton.setMenuRole(QAction.PreferencesRole) self.showEditorButton.setIconVisibleInMenu(True) self.showEditorButton.setToolTip(showEditor) self.showEditorButton.setText(showEditor) # Action for Clear button clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") self.clearButton = QAction(self) self.clearButton.setCheckable(False) self.clearButton.setEnabled(True) self.clearButton.setIcon(QgsApplication.getThemeIcon("console/iconClearConsole.svg")) self.clearButton.setMenuRole(QAction.PreferencesRole) self.clearButton.setIconVisibleInMenu(True) self.clearButton.setToolTip(clearBt) self.clearButton.setText(clearBt) # Action for settings optionsBt = QCoreApplication.translate("PythonConsole", "Options…") self.optionsButton = QAction(self) self.optionsButton.setCheckable(False) self.optionsButton.setEnabled(True) self.optionsButton.setIcon(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")) self.optionsButton.setMenuRole(QAction.PreferencesRole) self.optionsButton.setIconVisibleInMenu(True) self.optionsButton.setToolTip(optionsBt) self.optionsButton.setText(optionsBt) # Action for Run script runBt = QCoreApplication.translate("PythonConsole", "Run Command") self.runButton = QAction(self) self.runButton.setCheckable(False) self.runButton.setEnabled(True) self.runButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg")) self.runButton.setMenuRole(QAction.PreferencesRole) self.runButton.setIconVisibleInMenu(True) self.runButton.setToolTip(runBt) self.runButton.setText(runBt) # Help button self.helpConsoleAction = QAction(self) self.helpConsoleAction.setEnabled(True) self.helpConsoleAction.setText(QCoreApplication.translate("PythonConsole", "Python Console Help")) self.helpAPIAction = QAction(self) self.helpAPIAction.setEnabled(True) self.helpAPIAction.setText(QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")) self.helpCookbookAction = QAction(self) self.helpCookbookAction.setEnabled(True) self.helpCookbookAction.setText(QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")) self.helpMenu = QMenu(self) self.helpMenu.addAction(self.helpConsoleAction) self.helpMenu.addAction(self.helpAPIAction) self.helpMenu.addAction(self.helpCookbookAction) helpBt = QCoreApplication.translate("PythonConsole", "Help…") self.helpButton = QToolButton(self) self.helpButton.setPopupMode(QToolButton.InstantPopup) self.helpButton.setEnabled(True) self.helpButton.setIcon(QgsApplication.getThemeIcon("console/iconHelpConsole.svg")) self.helpButton.setToolTip(helpBt) self.helpButton.setMenu(self.helpMenu) self.toolBar = QToolBar() self.toolBar.setEnabled(True) self.toolBar.setFocusPolicy(Qt.NoFocus) self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBar.setLayoutDirection(Qt.LeftToRight) self.toolBar.setIconSize(icon_size) self.toolBar.setMovable(False) self.toolBar.setFloatable(False) self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.runButton) self.toolBar.addSeparator() self.toolBar.addAction(self.showEditorButton) self.toolBar.addSeparator() self.toolBar.addAction(self.optionsButton) self.toolBar.addWidget(self.helpButton) self.toolBarEditor = QToolBar() self.toolBarEditor.setEnabled(False) self.toolBarEditor.setFocusPolicy(Qt.NoFocus) self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBarEditor.setLayoutDirection(Qt.LeftToRight) self.toolBarEditor.setIconSize(icon_size) self.toolBarEditor.setMovable(False) self.toolBarEditor.setFloatable(False) self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.findTextButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.commentEditorButton) self.toolBarEditor.addAction(self.uncommentEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.objectListButton) self.widgetButton = QWidget() sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButton.sizePolicy().hasHeightForWidth()) self.widgetButton.setSizePolicy(sizePolicy) self.widgetButtonEditor = QWidget(self.widgetEditor) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.widgetButtonEditor.sizePolicy().hasHeightForWidth()) self.widgetButtonEditor.setSizePolicy(sizePolicy) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.shellOut.sizePolicy().hasHeightForWidth()) self.shellOut.setSizePolicy(sizePolicy) self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) # ------------ Layout ------------------------------- self.mainLayout = QGridLayout(self) self.mainLayout.setMargin(0) self.mainLayout.setSpacing(0) self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1) self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1) self.shellOutWidget.layout().insertWidget(0, self.toolBar) self.layoutEditor = QGridLayout(self.widgetEditor) self.layoutEditor.setMargin(0) self.layoutEditor.setSpacing(0) self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1) self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1) self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1) self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1) # Layout for the find widget self.layoutFind = QGridLayout(self.widgetFind) self.layoutFind.setContentsMargins(0, 0, 0, 0) self.lineEditFind = QgsFilterLineEdit() self.lineEditFind.setShowSearchIcon(True) placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find…") self.lineEditFind.setPlaceholderText(placeHolderTxt) self.toolBarFindText = QToolBar() self.toolBarFindText.setIconSize(icon_size) self.findNextButton = QAction(self) self.findNextButton.setEnabled(False) toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next") self.findNextButton.setToolTip(toolTipfindNext) self.findNextButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchNextEditorConsole.svg")) self.findPrevButton = QAction(self) self.findPrevButton.setEnabled(False) toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous") self.findPrevButton.setToolTip(toolTipfindPrev) self.findPrevButton.setIcon(QgsApplication.getThemeIcon("console/iconSearchPrevEditorConsole.svg")) self.caseSensitive = QCheckBox() caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive") self.caseSensitive.setText(caseSensTr) self.wholeWord = QCheckBox() wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word") self.wholeWord.setText(wholeWordTr) self.wrapAround = QCheckBox() self.wrapAround.setChecked(True) wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around") self.wrapAround.setText(wrapAroundTr) self.toolBarFindText.addWidget(self.lineEditFind) self.toolBarFindText.addAction(self.findPrevButton) self.toolBarFindText.addAction(self.findNextButton) self.toolBarFindText.addWidget(self.caseSensitive) self.toolBarFindText.addWidget(self.wholeWord) self.toolBarFindText.addWidget(self.wrapAround) self.layoutFind.addWidget(self.toolBarFindText, 0, 1, 1, 1) # ------------ Add first Tab in Editor ------------------------------- # self.tabEditorWidget.newTabEditor(tabName='first', filename=None) # ------------ Signal ------------------------------- self.findTextButton.triggered.connect(self._toggleFind) self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.commentEditorButton.triggered.connect(self.commentCode) self.uncommentEditorButton.triggered.connect(self.uncommentCode) self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.cutEditorButton.triggered.connect(self.cutEditor) self.copyEditorButton.triggered.connect(self.copyEditor) self.pasteEditorButton.triggered.connect(self.pasteEditor) self.showEditorButton.toggled.connect(self.toggleEditor) self.clearButton.triggered.connect(self.shellOut.clearConsole) self.optionsButton.triggered.connect(self.openSettings) self.runButton.triggered.connect(self.shell.entered) self.openFileButton.triggered.connect(self.openScriptFile) self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.saveFileButton.triggered.connect(self.saveScriptFile) self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.helpConsoleAction.triggered.connect(self.openHelpConsole) self.helpAPIAction.triggered.connect(self.openHelpAPI) self.helpCookbookAction.triggered.connect(self.openHelpCookbook) self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.lineEditFind.returnPressed.connect(self._findNext) self.findNextButton.triggered.connect(self._findNext) self.findPrevButton.triggered.connect(self._findPrev) self.lineEditFind.textChanged.connect(self._textFindChanged) self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._openFind) self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor) self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut) self.findNextScut.activated.connect(self._findNext) self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor) self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut) self.findPreviousScut.activated.connect(self._findPrev) # Escape on editor hides the find bar self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._closeFind) if iface is not None: self.exit_blocker = ConsoleExitBlocker(self) iface.registerApplicationExitBlocker(self.exit_blocker) def allowExit(self): tab_count = self.tabEditorWidget.count() for i in range(tab_count): # iterate backwards through tabs, as we may be closing some as we go tab_index = tab_count - i - 1 tab_widget = self.tabEditorWidget.widget(tab_index) if tab_widget.newEditor.isModified(): ret = QMessageBox.question(self, self.tr("Save {}").format(self.tabEditorWidget.tabText(tab_index)), self.tr("There are unsaved changes in this script. Do you want to keep those?"), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: tab_widget.save() if tab_widget.newEditor.isModified(): # save failed, treat as cancel return False elif ret == QMessageBox.Discard: pass else: return False self.tabEditorWidget.removeTab(tab_index) return True def _toggleFind(self): self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget() def _openFind(self): self.tabEditorWidget.currentWidget().newEditor.openFindWidget() def _closeFind(self): self.tabEditorWidget.currentWidget().newEditor.closeFindWidget() def _findNext(self): self.tabEditorWidget.currentWidget().newEditor.findText(True) def _findPrev(self): self.tabEditorWidget.currentWidget().newEditor.findText(False) def _textFindChanged(self): if self.lineEditFind.text(): self.findNextButton.setEnabled(True) self.findPrevButton.setEnabled(True) self.tabEditorWidget.currentWidget().newEditor.findText(True, showMessage=False, findFirst=True) else: self.lineEditFind.setStyleSheet('') self.findNextButton.setEnabled(False) self.findPrevButton.setEnabled(False) def onClickGoToLine(self, item, column): tabEditor = self.tabEditorWidget.currentWidget().newEditor if item.text(1) == 'syntaxError': check = tabEditor.syntaxCheck() if check and not tabEditor.isReadOnly(): self.tabEditorWidget.currentWidget().save() return linenr = int(item.text(1)) itemName = str(item.text(0)) charPos = itemName.find(' ') if charPos != -1: objName = itemName[0:charPos] else: objName = itemName tabEditor.goToLine(str.encode(objName), linenr) def toggleEditor(self, checked): self.splitterObj.show() if checked else self.splitterObj.hide() if not self.tabEditorWidget: self.tabEditorWidget.enableToolBarEditor(checked) self.tabEditorWidget.restoreTabsOrAddNew() def toggleObjectListWidget(self, checked): self.listClassMethod.show() if checked else self.listClassMethod.hide() def pasteEditor(self): self.tabEditorWidget.currentWidget().newEditor.paste() def cutEditor(self): self.tabEditorWidget.currentWidget().newEditor.cut() def copyEditor(self): self.tabEditorWidget.currentWidget().newEditor.copy() def runScriptEditor(self): self.tabEditorWidget.currentWidget().newEditor.runScriptCode() def commentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True) def uncommentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False) def openScriptFileExtEditor(self): tabWidget = self.tabEditorWidget.currentWidget() path = tabWidget.path import subprocess try: subprocess.Popen([os.environ['EDITOR'], path]) except KeyError: QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def openScriptFile(self): lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) openFileTr = QCoreApplication.translate("PythonConsole", "Open File") fileList, selected_filter = QFileDialog.getOpenFileNames( self, openFileTr, lastDirPath, "Script file (*.py)") if fileList: for pyFile in fileList: for i in range(self.tabEditorWidget.count()): tabWidget = self.tabEditorWidget.widget(i) if tabWidget.path == pyFile: self.tabEditorWidget.setCurrentWidget(tabWidget) break else: tabName = QFileInfo(pyFile).fileName() self.tabEditorWidget.newTabEditor(tabName, pyFile) lastDirPath = QFileInfo(pyFile).path() self.settings.setValue("pythonConsole/lastDirPath", pyFile) self.updateTabListScript(pyFile, action='append') def saveScriptFile(self): tabWidget = self.tabEditorWidget.currentWidget() try: tabWidget.save() except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) def saveAsScriptFile(self, index=None): tabWidget = self.tabEditorWidget.currentWidget() if not index: index = self.tabEditorWidget.currentIndex() if not tabWidget.path: fileName = self.tabEditorWidget.tabText(index) + '.py' folder = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) pathFileName = os.path.join(folder, fileName) fileNone = True else: pathFileName = tabWidget.path fileNone = False saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As") filename, filter = QFileDialog.getSaveFileName(self, saveAsFileTr, pathFileName, "Script file (*.py)") if filename: try: tabWidget.save(filename) except (IOError, OSError) as error: msgText = QCoreApplication.translate('PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) if fileNone: tabWidget.path = None else: tabWidget.path = pathFileName return if not fileNone: self.updateTabListScript(pathFileName, action='remove') def openHelpConsole(self): QgsHelp.openHelp("plugins/python_console.html") def openHelpAPI(self): m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION) if m: QDesktopServices.openUrl(QUrl('https://qgis.org/pyqgis/{}.{}/'.format(m.group(1), m.group(2)))) def openHelpCookbook(self): m = re.search(r'^([0-9]+)\.([0-9]+)\.', Qgis.QGIS_VERSION) if m: QDesktopServices.openUrl(QUrl('https://docs.qgis.org/{}.{}/en/docs/pyqgis_developer_cookbook/index.html'.format(m.group(1), m.group(2)))) def openSettings(self): iface.showOptionsDialog(iface.mainWindow(), currentPage='consoleOptions') def updateSettings(self): self.shell.refreshSettingsShell() self.shellOut.refreshSettingsOutput() self.tabEditorWidget.refreshSettingsEditor() def callWidgetMessageBar(self, text): self.shellOut.widgetMessageBar(iface, text) def callWidgetMessageBarEditor(self, text, level, timed): self.tabEditorWidget.widgetMessageBar(iface, text, level, timed) def updateTabListScript(self, script, action=None): if action == 'remove': self.tabListScript.remove(script) elif action == 'append': if not self.tabListScript: self.tabListScript = [] if script not in self.tabListScript: self.tabListScript.append(script) else: self.tabListScript = [] self.settings.setValue("pythonConsole/tabScripts", self.tabListScript) def saveSettingsConsole(self): self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState()) self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState()) self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState()) self.shell.writeHistoryFile(True) def restoreSettingsConsole(self): storedTabScripts = self.settings.value("pythonConsole/tabScripts", []) self.tabListScript = storedTabScripts self.splitter.restoreState(self.settings.value("pythonConsole/splitterConsole", QByteArray())) self.splitterEditor.restoreState(self.settings.value("pythonConsole/splitterEditor", QByteArray())) self.splitterObj.restoreState(self.settings.value("pythonConsole/splitterObj", QByteArray()))
def set_up_resource_parameters(self): """Set up the resource parameter for the add/edit view. """ name_parameter = StringParameter('UUID-1') name_parameter.name = self.resource_parameters['Resource name'] name_parameter.help_text = tr( 'Name of the resource that will be provided ' 'as part of minimum needs. ' 'e.g. Rice, Water etc.') name_parameter.description = tr( 'A <b>resource</b> is something that you provide to displaced ' 'persons in the event of a disaster. The resource will be made ' 'available at IDP camps and may need to be stockpiled by ' 'contingency planners in their preparations for a disaster.') name_parameter.is_required = True name_parameter.value = '' description_parameter = StringParameter('UUID-2') description_parameter.name = self.resource_parameters[ 'Resource description'] description_parameter.help_text = tr( 'Description of the resource that will be provided as part of ' 'minimum needs.') description_parameter.description = tr( 'This gives a detailed description of what the resource is and ') description_parameter.is_required = True description_parameter.value = '' unit_parameter = StringParameter('UUID-3') unit_parameter.name = self.resource_parameters['Unit'] unit_parameter.help_text = tr( 'Single unit for the resources spelled out. e.g. litre, ' 'kilogram etc.') unit_parameter.description = tr( 'A <b>unit</b> is the basic measurement unit used for computing ' 'the allowance per individual. For example when planning water ' 'rations the unit would be single litre.') unit_parameter.is_required = True unit_parameter.value = '' units_parameter = StringParameter('UUID-4') units_parameter.name = self.resource_parameters['Units'] units_parameter.help_text = tr( 'Multiple units for the resources spelled out. e.g. litres, ' 'kilogram etc.') units_parameter.description = tr( '<b>Units</b> are the basic measurement used for computing the ' 'allowance per individual. For example when planning water ' 'rations the units would be litres.') units_parameter.is_required = True units_parameter.value = '' unit_abbreviation_parameter = StringParameter('UUID-5') unit_abbreviation_parameter.name = \ self.resource_parameters['Unit abbreviation'] unit_abbreviation_parameter.help_text = tr( 'Abbreviations of unit for the resources. e.g. l, kg etc.') unit_abbreviation_parameter.description = tr( "A <b>unit abbreviation</b> is the basic measurement unit's " "shortened. For example when planning water rations " "the units would be l.") unit_abbreviation_parameter.is_required = True unit_abbreviation_parameter.value = '' minimum_parameter = FloatParameter('UUID-6') minimum_parameter.name = self.resource_parameters['Minimum allowed'] minimum_parameter.is_required = True minimum_parameter.precision = 6 minimum_parameter.minimum_allowed_value = 0.0 minimum_parameter.maximum_allowed_value = 99999.0 minimum_parameter.help_text = tr( 'The minimum allowable quantity per person. ') minimum_parameter.description = tr( 'The <b>minimum</b> is the minimum allowed quantity of the ' 'resource per person. For example you may dictate that the water ' 'ration per person per day should never be allowed to be less ' 'than 0.5l. This is enforced when tweaking a minimum needs set ' 'before an impact evaluation') minimum_parameter.value = 0.00 maximum_parameter = FloatParameter('UUID-7') maximum_parameter.name = self.resource_parameters['Maximum allowed'] maximum_parameter.is_required = True maximum_parameter.precision = 6 maximum_parameter.minimum_allowed_value = 0.0 maximum_parameter.maximum_allowed_value = 99999.0 maximum_parameter.help_text = tr( 'The maximum allowable quantity per person. ') maximum_parameter.description = tr( 'The <b>maximum</b> is the maximum allowed quantity of the ' 'resource per person. For example you may dictate that the water ' 'ration per person per day should never be allowed to be more ' 'than 67l. This is enforced when tweaking a maximum needs set ' 'before an impact evaluation.') maximum_parameter.value = 100.0 default_parameter = FloatParameter('UUID-8') default_parameter.name = self.resource_parameters['Default'] default_parameter.is_required = True default_parameter.precision = 6 default_parameter.minimum_allowed_value = 0.0 default_parameter.maximum_allowed_value = 99999.0 default_parameter.help_text = tr( 'The default allowable quantity per person. ') default_parameter.description = tr( "The <b>default</b> is the default allowed quantity of the " "resource per person. For example you may indicate that the water " "ration per person weekly should be 67l.") default_parameter.value = 10.0 frequency_parameter = StringParameter('UUID-9') frequency_parameter.name = self.resource_parameters['Frequency'] frequency_parameter.help_text = tr( "The frequency that this resource needs to be provided to a " "displaced person. e.g. weekly, daily, once etc.") frequency_parameter.description = tr( "The <b>frequency</b> informs the aid worker how regularly this " "resource needs to be provided to the displaced person.") frequency_parameter.is_required = True frequency_parameter.value = tr('weekly') sentence_parameter = TextParameter('UUID-10') sentence_parameter.name = self.resource_parameters['Readable sentence'] sentence_parameter.help_text = tr( 'A readable presentation of the resource.') sentence_parameter.description = tr( "A <b>readable sentence</b> is a presentation of the resource " "that displays all pertinent information. If you are unsure then " "use the default. Properties should be included using double " "curly brackets '{{' '}}'. Including the resource name would be " "achieved by including e.g. {{ Resource name }}") sentence_parameter.is_required = True sentence_parameter.value = tr( 'A displaced person should be provided with ' '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of ' '%(resource name)s. Though no less than %(minimum allowed)s ' 'and no more than %(maximum allowed)s. This should be provided ' '%(frequency)s.' % { 'default value': '{{ Default }}', 'unit': '{{ Unit }}', 'units': '{{ Units }}', 'unit abbreviation': '{{ Unit abbreviation }}', 'resource name': '{{ Resource name }}', 'minimum allowed': '{{ Minimum allowed }}', 'maximum allowed': '{{ Maximum allowed }}', 'frequency': '{{ Frequency }}' }) parameters = [ name_parameter, description_parameter, unit_parameter, units_parameter, unit_abbreviation_parameter, default_parameter, minimum_parameter, maximum_parameter, frequency_parameter, sentence_parameter ] parameter_container = ParameterContainer(parameters) parameter_container.setup_ui() layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(parameter_container) self.parameters_scrollarea.setLayout(layout)
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 DefaultSelectParameterWidget(SelectParameterWidget): """Widget class for Default Select Parameter.""" def __init__(self, parameter, parent=None): """Constructor :param parameter: A DefaultSelectParameter object. :type parameter: DefaultSelectParameter """ super(DefaultSelectParameterWidget, self).__init__(parameter, parent) self.default_layout = QHBoxLayout() self.radio_button_layout = QHBoxLayout() self.radio_button_widget = QWidget() self.default_label = QLabel(tr('Default')) # Create radio button group self.default_input_button_group = QButtonGroup() # Define string enabler for radio button self.radio_button_enabler = self.input.itemData(0, Qt.UserRole) for i in range(len(self._parameter.default_labels)): if '%s' in self._parameter.default_labels[i]: label = (self._parameter.default_labels[i] % self._parameter.default_values[i]) else: label = self._parameter.default_labels[i] radio_button = QRadioButton(label) self.radio_button_layout.addWidget(radio_button) self.default_input_button_group.addButton(radio_button, i) if self._parameter.default_value == \ self._parameter.default_values[i]: radio_button.setChecked(True) # Create double spin box for custom value self.custom_value = QDoubleSpinBox() if self._parameter.default_values[-1]: self.custom_value.setValue(self._parameter.default_values[-1]) has_min = False if self._parameter.minimum is not None: has_min = True self.custom_value.setMinimum(self._parameter.minimum) has_max = False if self._parameter.maximum is not None: has_max = True self.custom_value.setMaximum(self._parameter.maximum) if has_min and has_max: step = (self._parameter.maximum - self._parameter.minimum) / 100.0 self.custom_value.setSingleStep(step) self.radio_button_layout.addWidget(self.custom_value) self.toggle_custom_value() # Reset the layout self.input_layout.setParent(None) self.help_layout.setParent(None) self.label.setParent(None) self.inner_input_layout.setParent(None) self.input_layout = QGridLayout() self.input_layout.setSpacing(0) self.input_layout.addWidget(self.label, 0, 0) self.input_layout.addLayout(self.inner_input_layout, 0, 1) self.input_layout.addWidget(self.default_label, 1, 0) self.input_layout.addLayout(self.radio_button_layout, 1, 1) self.main_layout.addLayout(self.input_layout) self.main_layout.addLayout(self.help_layout) # check every added combobox, it could have been toggled by # the existing keyword self.toggle_input() # Connect # noinspection PyUnresolvedReferences self.input.currentIndexChanged.connect(self.toggle_input) self.default_input_button_group.buttonClicked.connect( self.toggle_custom_value) def raise_invalid_type_exception(self): message = 'Expecting element type of %s' % ( self._parameter.element_type.__name__) err = ValueError(message) return err def get_parameter(self): """Obtain list parameter object from the current widget state. :returns: A DefaultSelectParameter from the current state of widget. """ current_index = self.input.currentIndex() selected_value = self.input.itemData(current_index, Qt.UserRole) if hasattr(selected_value, 'toPyObject'): selected_value = selected_value.toPyObject() try: self._parameter.value = selected_value except ValueError: err = self.raise_invalid_type_exception() raise err radio_button_checked_id = self.default_input_button_group.checkedId() # No radio button checked, then default value = None if radio_button_checked_id == -1: self._parameter.default = None # The last radio button (custom) is checked, get the value from the # line edit elif (radio_button_checked_id == len(self._parameter.default_values) - 1): self._parameter.default_values[radio_button_checked_id] \ = self.custom_value.value() self._parameter.default = self.custom_value.value() else: self._parameter.default = self._parameter.default_values[ radio_button_checked_id] return self._parameter def set_default(self, default): """Set default value by item's string. :param default: The default. :type default: str, int :returns: True if success, else False. :rtype: bool """ # Find index of choice try: default_index = self._parameter.default_values.index(default) self.default_input_button_group.button(default_index).setChecked( True) except ValueError: last_index = len(self._parameter.default_values) - 1 self.default_input_button_group.button(last_index).setChecked(True) self.custom_value.setValue(default) self.toggle_custom_value() def toggle_custom_value(self): radio_button_checked_id = self.default_input_button_group.checkedId() if (radio_button_checked_id == len(self._parameter.default_values) - 1): self.custom_value.setDisabled(False) else: self.custom_value.setDisabled(True) def toggle_input(self): """Change behaviour of radio button based on input.""" current_index = self.input.currentIndex() # If current input is not a radio button enabler, disable radio button. if self.input.itemData(current_index, Qt.UserRole) != (self.radio_button_enabler): self.disable_radio_button() # Otherwise, enable radio button. else: self.enable_radio_button() def set_selected_radio_button(self): """Set selected radio button to 'Do not report'.""" dont_use_button = self.default_input_button_group.button( len(self._parameter.default_values) - 2) dont_use_button.setChecked(True) def disable_radio_button(self): """Disable radio button group and custom value input area.""" checked = self.default_input_button_group.checkedButton() if checked: self.default_input_button_group.setExclusive(False) checked.setChecked(False) self.default_input_button_group.setExclusive(True) for button in self.default_input_button_group.buttons(): button.setDisabled(True) self.custom_value.setDisabled(True) def enable_radio_button(self): """Enable radio button and custom value input area then set selected radio button to 'Do not report'. """ for button in self.default_input_button_group.buttons(): button.setEnabled(True) self.set_selected_radio_button() self.custom_value.setEnabled(True)
class ShellOutputScintilla(QsciScintilla): DEFAULT_COLOR = "#4d4d4c" KEYWORD_COLOR = "#8959a8" CLASS_COLOR = "#4271ae" METHOD_COLOR = "#4271ae" DECORATION_COLOR = "#3e999f" NUMBER_COLOR = "#c82829" COMMENT_COLOR = "#8e908c" COMMENT_BLOCK_COLOR = "#8e908c" BACKGROUND_COLOR = "#ffffff" CURSOR_COLOR = "#636363" CARET_LINE_COLOR = "#efefef" SINGLE_QUOTE_COLOR = "#718c00" DOUBLE_QUOTE_COLOR = "#718c00" TRIPLE_SINGLE_QUOTE_COLOR = "#eab700" TRIPLE_DOUBLE_QUOTE_COLOR = "#eab700" MARGIN_BACKGROUND_COLOR = "#efefef" MARGIN_FOREGROUND_COLOR = "#636363" SELECTION_BACKGROUND_COLOR = "#d7d7d7" SELECTION_FOREGROUND_COLOR = "#303030" MATCHED_BRACE_BACKGROUND_COLOR = "#b7f907" MATCHED_BRACE_FOREGROUND_COLOR = "#303030" def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can lead to data loss and/or leak") # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() self.setSelectionForegroundColor(QColor(self.settings.value("pythonConsole/selectionForegroundColor", QColor(self.SELECTION_FOREGROUND_COLOR)))) self.setSelectionBackgroundColor(QColor(self.settings.value("pythonConsole/selectionBackgroundColor", QColor(self.SELECTION_BACKGROUND_COLOR)))) self.setMarginsForegroundColor(QColor(self.settings.value("pythonConsole/marginForegroundColor", QColor(self.MARGIN_FOREGROUND_COLOR)))) self.setMarginsBackgroundColor(QColor(self.settings.value("pythonConsole/marginBackgroundColor", QColor(self.MARGIN_BACKGROUND_COLOR)))) caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor(self.CARET_LINE_COLOR)) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(self.CURSOR_COLOR)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(self.DEFAULT_COLOR)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(self.COMMENT_COLOR))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/numberFontColor", QColor(self.NUMBER_COLOR))), 2) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(self.KEYWORD_COLOR))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(self.CLASS_COLOR))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(self.METHOD_COLOR))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(self.DECORATION_COLOR))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(self.COMMENT_BLOCK_COLOR))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(self.SINGLE_QUOTE_COLOR))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(self.DOUBLE_QUOTE_COLOR))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(self.TRIPLE_SINGLE_QUOTE_COLOR))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(self.TRIPLE_DOUBLE_QUOTE_COLOR))), 7) self.lexer.setColor(QColor(self.settings.value("pythonConsole/defaultFontColorEditor", QColor(self.DEFAULT_COLOR))), 13) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.lexer.setFont(font, QsciLexerPython.UnclosedString) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(self.BACKGROUND_COLOR))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.svg") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.svg") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.svg") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QgsSettings() self.restoreGeometry( settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState( settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.toolBar.setIconSize(self.iface.iconSize()) self.toolBarOrientation() self.toolBar.orientationChanged.connect(self.toolBarOrientation) self.tabs.currentChanged.connect(self.tabChanged) self.tree.selectedItemChanged.connect(self.itemChanged) self.tree.model().dataChanged.connect(self.iface.reloadConnections) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) # save the window state settings = QgsSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): with OverrideCursor(Qt.WaitCursor): try: if item is None: item = self.tree.currentItem() self.tree.refreshItem( item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) def itemChanged(self, item): with OverrideCursor(Qt.WaitCursor): try: self.reloadButtons() # Force-reload information on the layer self.info.setDirty() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): with OverrideCursor(Qt.WaitCursor): try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled( self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), Qgis.Info, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage( self.tr("Select the table you want export to file."), Qgis.Info, self.iface.messageTimeout()) return inLayer = table.toMapLayer() if inLayer.type() != QgsMapLayerType.VectorLayer: self.infoBar.pushMessage( self.tr("Select a vector or a tabular layer you want export."), Qgis.Warning, self.iface.messageTimeout()) return from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), Qgis.Info, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from .dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query ({0})").format(dbname) index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect( functools.partial(self.update_query_tab_name, index, dbname)) def runSqlLayerWindow(self, layer): from .dlg_sql_layer_window import DlgSqlLayerWindow query = DlgSqlLayerWindow(self.iface, layer, self) lname = layer.name() tabname = self.tr("Layer ({0})").format(lname) index = self.tabs.addTab(query, tabname) # self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: def invoke_callback(x): return self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName( ).endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ with OverrideCursor(Qt.WaitCursor): try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[ i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: if hasattr(widget, "close"): if widget.close(): self.tabs.removeTab(index) widget.deleteLater() else: self.tabs.removeTab(index) widget.deleteLater() def toolBarOrientation(self): button_style = Qt.ToolButtonIconOnly if self.toolBar.orientation() == Qt.Horizontal: button_style = Qt.ToolButtonTextBesideIcon widget = self.toolBar.widgetForAction(self.actionImport) widget.setToolButtonStyle(button_style) widget = self.toolBar.widgetForAction(self.actionExport) widget.setToolButtonStyle(button_style) def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton( i, QTabBar.RightSide) else tabbar.tabButton( i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget(self.tr("Providers"), self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar(self.tr("Default"), self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction( QgsApplication.getThemeIcon("/mActionRefresh.svg"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction( QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL Window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction( QIcon(":/db_manager/actions/import"), QApplication.translate("DBManager", "&Import Layer/File…"), self.importActionSlot) self.actionExport = self.menuTable.addAction( QIcon(":/db_manager/actions/export"), QApplication.translate("DBManager", "&Export to File…"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addSeparator() self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
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 ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info" ) # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.svg") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.svg") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.svg") menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class PythonConsoleWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle( QCoreApplication.translate("PythonConsole", "Python Console")) self.settings = QSettings() self.shell = ShellScintilla(self) self.setFocusProxy(self.shell) self.shellOut = ShellOutputScintilla(self) self.tabEditorWidget = EditorTabWidget(self) ##------------ UI ------------------------------- self.splitterEditor = QSplitter(self) self.splitterEditor.setOrientation(Qt.Horizontal) self.splitterEditor.setHandleWidth(6) self.splitterEditor.setChildrenCollapsible(True) self.shellOutWidget = QWidget(self) self.shellOutWidget.setLayout(QVBoxLayout()) self.shellOutWidget.layout().setContentsMargins(0, 0, 0, 0) self.shellOutWidget.layout().addWidget(self.shellOut) self.splitter = QSplitter(self.splitterEditor) self.splitter.setOrientation(Qt.Vertical) self.splitter.setHandleWidth(3) self.splitter.setChildrenCollapsible(False) self.splitter.addWidget(self.shellOutWidget) self.splitter.addWidget(self.shell) #self.splitterEditor.addWidget(self.tabEditorWidget) self.splitterObj = QSplitter(self.splitterEditor) self.splitterObj.setHandleWidth(3) self.splitterObj.setOrientation(Qt.Horizontal) #self.splitterObj.setSizes([0, 0]) #self.splitterObj.setStretchFactor(0, 1) self.widgetEditor = QWidget(self.splitterObj) self.widgetFind = QWidget(self) self.listClassMethod = QTreeWidget(self.splitterObj) self.listClassMethod.setColumnCount(2) objInspLabel = QCoreApplication.translate("PythonConsole", "Object Inspector") self.listClassMethod.setHeaderLabels([objInspLabel, '']) self.listClassMethod.setColumnHidden(1, True) self.listClassMethod.setAlternatingRowColors(True) #self.splitterEditor.addWidget(self.widgetEditor) #self.splitterObj.addWidget(self.listClassMethod) #self.splitterObj.addWidget(self.widgetEditor) # Hide side editor on start up self.splitterObj.hide() self.listClassMethod.hide() # Hide search widget on start up self.widgetFind.hide() sizes = self.splitter.sizes() self.splitter.setSizes(sizes) ##----------------Restore Settings------------------------------------ self.restoreSettingsConsole() ##------------------Toolbar Editor------------------------------------- ## Action for Open File openFileBt = QCoreApplication.translate("PythonConsole", "Open Script...") self.openFileButton = QAction(self) self.openFileButton.setCheckable(False) self.openFileButton.setEnabled(True) self.openFileButton.setIcon( QgsApplication.getThemeIcon("console/iconOpenConsole.png")) self.openFileButton.setMenuRole(QAction.PreferencesRole) self.openFileButton.setIconVisibleInMenu(True) self.openFileButton.setToolTip(openFileBt) self.openFileButton.setText(openFileBt) openExtEditorBt = QCoreApplication.translate( "PythonConsole", "Open in External Editor") self.openInEditorButton = QAction(self) self.openInEditorButton.setCheckable(False) self.openInEditorButton.setEnabled(True) self.openInEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.openInEditorButton.setMenuRole(QAction.PreferencesRole) self.openInEditorButton.setIconVisibleInMenu(True) self.openInEditorButton.setToolTip(openExtEditorBt) self.openInEditorButton.setText(openExtEditorBt) ## Action for Save File saveFileBt = QCoreApplication.translate("PythonConsole", "Save") self.saveFileButton = QAction(self) self.saveFileButton.setCheckable(False) self.saveFileButton.setEnabled(False) self.saveFileButton.setIcon( QgsApplication.getThemeIcon("console/iconSaveConsole.png")) self.saveFileButton.setMenuRole(QAction.PreferencesRole) self.saveFileButton.setIconVisibleInMenu(True) self.saveFileButton.setToolTip(saveFileBt) self.saveFileButton.setText(saveFileBt) ## Action for Save File As saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As...") self.saveAsFileButton = QAction(self) self.saveAsFileButton.setCheckable(False) self.saveAsFileButton.setEnabled(True) self.saveAsFileButton.setIcon( QgsApplication.getThemeIcon("console/iconSaveAsConsole.png")) self.saveAsFileButton.setMenuRole(QAction.PreferencesRole) self.saveAsFileButton.setIconVisibleInMenu(True) self.saveAsFileButton.setToolTip(saveAsFileBt) self.saveAsFileButton.setText(saveAsFileBt) ## Action Cut cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") self.cutEditorButton = QAction(self) self.cutEditorButton.setCheckable(False) self.cutEditorButton.setEnabled(True) self.cutEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cutEditorButton.setMenuRole(QAction.PreferencesRole) self.cutEditorButton.setIconVisibleInMenu(True) self.cutEditorButton.setToolTip(cutEditorBt) self.cutEditorButton.setText(cutEditorBt) ## Action Copy copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") self.copyEditorButton = QAction(self) self.copyEditorButton.setCheckable(False) self.copyEditorButton.setEnabled(True) self.copyEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditCopy.svg")) self.copyEditorButton.setMenuRole(QAction.PreferencesRole) self.copyEditorButton.setIconVisibleInMenu(True) self.copyEditorButton.setToolTip(copyEditorBt) self.copyEditorButton.setText(copyEditorBt) ## Action Paste pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") self.pasteEditorButton = QAction(self) self.pasteEditorButton.setCheckable(False) self.pasteEditorButton.setEnabled(True) self.pasteEditorButton.setIcon( QgsApplication.getThemeIcon("mActionEditPaste.svg")) self.pasteEditorButton.setMenuRole(QAction.PreferencesRole) self.pasteEditorButton.setIconVisibleInMenu(True) self.pasteEditorButton.setToolTip(pasteEditorBt) self.pasteEditorButton.setText(pasteEditorBt) ## Action Run Script (subprocess) runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run script") self.runScriptEditorButton = QAction(self) self.runScriptEditorButton.setCheckable(False) self.runScriptEditorButton.setEnabled(True) self.runScriptEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconRunScriptConsole.png")) self.runScriptEditorButton.setMenuRole(QAction.PreferencesRole) self.runScriptEditorButton.setIconVisibleInMenu(True) self.runScriptEditorButton.setToolTip(runScriptEditorBt) self.runScriptEditorButton.setText(runScriptEditorBt) ## Action Run Script (subprocess) commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment") self.commentEditorButton = QAction(self) self.commentEditorButton.setCheckable(False) self.commentEditorButton.setEnabled(True) self.commentEditorButton.setIcon( QgsApplication.getThemeIcon( "console/iconCommentEditorConsole.png")) self.commentEditorButton.setMenuRole(QAction.PreferencesRole) self.commentEditorButton.setIconVisibleInMenu(True) self.commentEditorButton.setToolTip(commentEditorBt) self.commentEditorButton.setText(commentEditorBt) ## Action Run Script (subprocess) uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment") self.uncommentEditorButton = QAction(self) self.uncommentEditorButton.setCheckable(False) self.uncommentEditorButton.setEnabled(True) self.uncommentEditorButton.setIcon( QgsApplication.getThemeIcon( "console/iconUncommentEditorConsole.png")) self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole) self.uncommentEditorButton.setIconVisibleInMenu(True) self.uncommentEditorButton.setToolTip(uncommentEditorBt) self.uncommentEditorButton.setText(uncommentEditorBt) ## Action for Object browser objList = QCoreApplication.translate("PythonConsole", "Object Inspector...") self.objectListButton = QAction(self) self.objectListButton.setCheckable(True) self.objectListButton.setEnabled( self.settings.value("pythonConsole/enableObjectInsp", False, type=bool)) self.objectListButton.setIcon( QgsApplication.getThemeIcon("console/iconClassBrowserConsole.png")) self.objectListButton.setMenuRole(QAction.PreferencesRole) self.objectListButton.setIconVisibleInMenu(True) self.objectListButton.setToolTip(objList) self.objectListButton.setText(objList) ## Action for Find text findText = QCoreApplication.translate("PythonConsole", "Find Text") self.findTextButton = QAction(self) self.findTextButton.setCheckable(True) self.findTextButton.setEnabled(True) self.findTextButton.setIcon( QgsApplication.getThemeIcon("console/iconSearchEditorConsole.png")) self.findTextButton.setMenuRole(QAction.PreferencesRole) self.findTextButton.setIconVisibleInMenu(True) self.findTextButton.setToolTip(findText) self.findTextButton.setText(findText) ##----------------Toolbar Console------------------------------------- ## Action Show Editor showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") self.showEditorButton = QAction(self) self.showEditorButton.setEnabled(True) self.showEditorButton.setCheckable(True) self.showEditorButton.setIcon( QgsApplication.getThemeIcon("console/iconShowEditorConsole.png")) self.showEditorButton.setMenuRole(QAction.PreferencesRole) self.showEditorButton.setIconVisibleInMenu(True) self.showEditorButton.setToolTip(showEditor) self.showEditorButton.setText(showEditor) ## Action for Clear button clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") self.clearButton = QAction(self) self.clearButton.setCheckable(False) self.clearButton.setEnabled(True) self.clearButton.setIcon( QgsApplication.getThemeIcon("console/iconClearConsole.png")) self.clearButton.setMenuRole(QAction.PreferencesRole) self.clearButton.setIconVisibleInMenu(True) self.clearButton.setToolTip(clearBt) self.clearButton.setText(clearBt) ## Action for settings optionsBt = QCoreApplication.translate("PythonConsole", "Options...") self.optionsButton = QAction(self) self.optionsButton.setCheckable(False) self.optionsButton.setEnabled(True) self.optionsButton.setIcon( QgsApplication.getThemeIcon("console/iconSettingsConsole.png")) self.optionsButton.setMenuRole(QAction.PreferencesRole) self.optionsButton.setIconVisibleInMenu(True) self.optionsButton.setToolTip(optionsBt) self.optionsButton.setText(optionsBt) ## Action menu for class actionClassBt = QCoreApplication.translate("PythonConsole", "Import Class") self.actionClass = QAction(self) self.actionClass.setCheckable(False) self.actionClass.setEnabled(True) self.actionClass.setIcon( QgsApplication.getThemeIcon("console/iconClassConsole.png")) self.actionClass.setMenuRole(QAction.PreferencesRole) self.actionClass.setIconVisibleInMenu(True) self.actionClass.setToolTip(actionClassBt) self.actionClass.setText(actionClassBt) ## Action for Run script runBt = QCoreApplication.translate("PythonConsole", "Run Command") self.runButton = QAction(self) self.runButton.setCheckable(False) self.runButton.setEnabled(True) self.runButton.setIcon( QgsApplication.getThemeIcon("console/iconRunConsole.png")) self.runButton.setMenuRole(QAction.PreferencesRole) self.runButton.setIconVisibleInMenu(True) self.runButton.setToolTip(runBt) self.runButton.setText(runBt) ## Help action helpBt = QCoreApplication.translate("PythonConsole", "Help...") self.helpButton = QAction(self) self.helpButton.setCheckable(False) self.helpButton.setEnabled(True) self.helpButton.setIcon( QgsApplication.getThemeIcon("console/iconHelpConsole.png")) self.helpButton.setMenuRole(QAction.PreferencesRole) self.helpButton.setIconVisibleInMenu(True) self.helpButton.setToolTip(helpBt) self.helpButton.setText(helpBt) self.toolBar = QToolBar() self.toolBar.setEnabled(True) self.toolBar.setFocusPolicy(Qt.NoFocus) self.toolBar.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBar.setLayoutDirection(Qt.LeftToRight) self.toolBar.setIconSize(QSize(16, 16)) self.toolBar.setMovable(False) self.toolBar.setFloatable(False) self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.actionClass) self.toolBar.addAction(self.runButton) self.toolBar.addSeparator() self.toolBar.addAction(self.showEditorButton) self.toolBar.addSeparator() self.toolBar.addAction(self.optionsButton) self.toolBar.addAction(self.helpButton) self.toolBarEditor = QToolBar() self.toolBarEditor.setEnabled(False) self.toolBarEditor.setFocusPolicy(Qt.NoFocus) self.toolBarEditor.setContextMenuPolicy(Qt.DefaultContextMenu) self.toolBarEditor.setLayoutDirection(Qt.LeftToRight) self.toolBarEditor.setIconSize(QSize(16, 16)) self.toolBarEditor.setMovable(False) self.toolBarEditor.setFloatable(False) self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.findTextButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.commentEditorButton) self.toolBarEditor.addAction(self.uncommentEditorButton) self.toolBarEditor.addSeparator() self.toolBarEditor.addAction(self.objectListButton) ## Menu Import Class default_command = { (QCoreApplication.translate("PythonConsole", "Import Processing Class"), QgsApplication.getThemeIcon("console/iconProcessingConsole.png")): ["import processing"], (QCoreApplication.translate("PythonConsole", "Import PyQt.QtCore Class"), QgsApplication.getThemeIcon("console/iconQtCoreConsole.png")): ["from qgis.PyQt.QtCore import *"], (QCoreApplication.translate("PythonConsole", "Import PyQt.QtGui Class"), QgsApplication.getThemeIcon("console/iconQtGuiConsole.png")): [ "from qgis.PyQt.QtGui import *", "from qgis.PyQt.QtWidgets import *" ] } self.classMenu = QMenu() for (title, icon), commands in default_command.items(): action = self.classMenu.addAction(icon, title) action.triggered.connect( partial(self.shell.commandConsole, commands)) cM = self.toolBar.widgetForAction(self.actionClass) cM.setMenu(self.classMenu) cM.setPopupMode(QToolButton.InstantPopup) self.widgetButton = QWidget() sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.widgetButton.sizePolicy().hasHeightForWidth()) self.widgetButton.setSizePolicy(sizePolicy) self.widgetButtonEditor = QWidget(self.widgetEditor) sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.widgetButtonEditor.sizePolicy().hasHeightForWidth()) self.widgetButtonEditor.setSizePolicy(sizePolicy) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.shellOut.sizePolicy().hasHeightForWidth()) self.shellOut.setSizePolicy(sizePolicy) self.shellOut.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.shell.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) ##------------ Layout ------------------------------- self.mainLayout = QGridLayout(self) self.mainLayout.setMargin(0) self.mainLayout.setSpacing(0) self.mainLayout.addWidget(self.widgetButton, 0, 0, 1, 1) self.mainLayout.addWidget(self.splitterEditor, 0, 1, 1, 1) self.shellOutWidget.layout().insertWidget(0, self.toolBar) self.layoutEditor = QGridLayout(self.widgetEditor) self.layoutEditor.setMargin(0) self.layoutEditor.setSpacing(0) self.layoutEditor.addWidget(self.toolBarEditor, 0, 1, 1, 1) self.layoutEditor.addWidget(self.widgetButtonEditor, 1, 0, 2, 1) self.layoutEditor.addWidget(self.tabEditorWidget, 1, 1, 1, 1) self.layoutEditor.addWidget(self.widgetFind, 2, 1, 1, 1) ## Layout for the find widget self.layoutFind = QGridLayout(self.widgetFind) self.layoutFind.setContentsMargins(0, 0, 0, 0) self.lineEditFind = QgsFilterLineEdit() placeHolderTxt = QCoreApplication.translate("PythonConsole", "Enter text to find...") self.lineEditFind.setPlaceholderText(placeHolderTxt) self.findNextButton = QToolButton() self.findNextButton.setEnabled(False) toolTipfindNext = QCoreApplication.translate("PythonConsole", "Find Next") self.findNextButton.setToolTip(toolTipfindNext) self.findNextButton.setIcon( QgsApplication.getThemeIcon( "console/iconSearchNextEditorConsole.png")) self.findNextButton.setIconSize(QSize(24, 24)) self.findNextButton.setAutoRaise(True) self.findPrevButton = QToolButton() self.findPrevButton.setEnabled(False) toolTipfindPrev = QCoreApplication.translate("PythonConsole", "Find Previous") self.findPrevButton.setToolTip(toolTipfindPrev) self.findPrevButton.setIcon( QgsApplication.getThemeIcon( "console/iconSearchPrevEditorConsole.png")) self.findPrevButton.setIconSize(QSize(24, 24)) self.findPrevButton.setAutoRaise(True) self.caseSensitive = QCheckBox() caseSensTr = QCoreApplication.translate("PythonConsole", "Case Sensitive") self.caseSensitive.setText(caseSensTr) self.wholeWord = QCheckBox() wholeWordTr = QCoreApplication.translate("PythonConsole", "Whole Word") self.wholeWord.setText(wholeWordTr) self.wrapAround = QCheckBox() self.wrapAround.setChecked(True) wrapAroundTr = QCoreApplication.translate("PythonConsole", "Wrap Around") self.wrapAround.setText(wrapAroundTr) self.layoutFind.addWidget(self.lineEditFind, 0, 1, 1, 1) self.layoutFind.addWidget(self.findPrevButton, 0, 2, 1, 1) self.layoutFind.addWidget(self.findNextButton, 0, 3, 1, 1) self.layoutFind.addWidget(self.caseSensitive, 0, 4, 1, 1) self.layoutFind.addWidget(self.wholeWord, 0, 5, 1, 1) self.layoutFind.addWidget(self.wrapAround, 0, 6, 1, 1) ##------------ Add first Tab in Editor ------------------------------- #self.tabEditorWidget.newTabEditor(tabName='first', filename=None) ##------------ Signal ------------------------------- self.findTextButton.triggered.connect(self._toggleFind) self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.commentEditorButton.triggered.connect(self.commentCode) self.uncommentEditorButton.triggered.connect(self.uncommentCode) self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.cutEditorButton.triggered.connect(self.cutEditor) self.copyEditorButton.triggered.connect(self.copyEditor) self.pasteEditorButton.triggered.connect(self.pasteEditor) self.showEditorButton.toggled.connect(self.toggleEditor) self.clearButton.triggered.connect(self.shellOut.clearConsole) self.optionsButton.triggered.connect(self.openSettings) self.runButton.triggered.connect(self.shell.entered) self.openFileButton.triggered.connect(self.openScriptFile) self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.saveFileButton.triggered.connect(self.saveScriptFile) self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.helpButton.triggered.connect(self.openHelp) self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.lineEditFind.returnPressed.connect(self._findNext) self.findNextButton.clicked.connect(self._findNext) self.findPrevButton.clicked.connect(self._findPrev) self.lineEditFind.textChanged.connect(self._textFindChanged) self.findScut = QShortcut(QKeySequence.Find, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._openFind) self.findNextScut = QShortcut(QKeySequence.FindNext, self.widgetEditor) self.findNextScut.setContext(Qt.WidgetWithChildrenShortcut) self.findNextScut.activated.connect(self._findNext) self.findPreviousScut = QShortcut(QKeySequence.FindPrevious, self.widgetEditor) self.findPreviousScut.setContext(Qt.WidgetWithChildrenShortcut) self.findPreviousScut.activated.connect(self._findPrev) # Escape on editor hides the find bar self.findScut = QShortcut(Qt.Key_Escape, self.widgetEditor) self.findScut.setContext(Qt.WidgetWithChildrenShortcut) self.findScut.activated.connect(self._closeFind) def _toggleFind(self): self.tabEditorWidget.currentWidget().newEditor.toggleFindWidget() def _openFind(self): self.tabEditorWidget.currentWidget().newEditor.openFindWidget() def _closeFind(self): self.tabEditorWidget.currentWidget().newEditor.closeFindWidget() def _findNext(self): self.tabEditorWidget.currentWidget().newEditor.findText(True) def _findPrev(self): self.tabEditorWidget.currentWidget().newEditor.findText(False) def _textFindChanged(self): if self.lineEditFind.text(): self.findNextButton.setEnabled(True) self.findPrevButton.setEnabled(True) self.tabEditorWidget.currentWidget().newEditor.findText( True, showMessage=False, findFirst=True) else: self.lineEditFind.setStyleSheet('') self.findNextButton.setEnabled(False) self.findPrevButton.setEnabled(False) def onClickGoToLine(self, item, column): tabEditor = self.tabEditorWidget.currentWidget().newEditor if item.text(1) == 'syntaxError': check = tabEditor.syntaxCheck(fromContextMenu=False) if check and not tabEditor.isReadOnly(): self.tabEditorWidget.currentWidget().save() return linenr = int(item.text(1)) itemName = str(item.text(0)) charPos = itemName.find(' ') if charPos != -1: objName = itemName[0:charPos] else: objName = itemName tabEditor.goToLine(objName, linenr) def toggleEditor(self, checked): self.splitterObj.show() if checked else self.splitterObj.hide() if not self.tabEditorWidget: self.tabEditorWidget.enableToolBarEditor(checked) self.tabEditorWidget.restoreTabsOrAddNew() def toggleObjectListWidget(self, checked): self.listClassMethod.show() if checked else self.listClassMethod.hide() def pasteEditor(self): self.tabEditorWidget.currentWidget().newEditor.paste() def cutEditor(self): self.tabEditorWidget.currentWidget().newEditor.cut() def copyEditor(self): self.tabEditorWidget.currentWidget().newEditor.copy() def runScriptEditor(self): self.tabEditorWidget.currentWidget().newEditor.runScriptCode() def commentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True) def uncommentCode(self): self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False) def openScriptFileExtEditor(self): tabWidget = self.tabEditorWidget.currentWidget() path = tabWidget.path import subprocess try: subprocess.Popen([os.environ['EDITOR'], path]) except KeyError: QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def openScriptFile(self): lastDirPath = self.settings.value("pythonConsole/lastDirPath", QDir.homePath()) openFileTr = QCoreApplication.translate("PythonConsole", "Open File") fileList = QFileDialog.getOpenFileNames(self, openFileTr, lastDirPath, "Script file (*.py)") if fileList: for pyFile in fileList: for i in range(self.tabEditorWidget.count()): tabWidget = self.tabEditorWidget.widget(i) if tabWidget.path == pyFile: self.tabEditorWidget.setCurrentWidget(tabWidget) break else: tabName = QFileInfo(pyFile).fileName() self.tabEditorWidget.newTabEditor(tabName, pyFile) lastDirPath = QFileInfo(pyFile).path() self.settings.setValue("pythonConsole/lastDirPath", pyFile) self.updateTabListScript(pyFile, action='append') def saveScriptFile(self): tabWidget = self.tabEditorWidget.currentWidget() try: tabWidget.save() except (IOError, OSError) as error: msgText = QCoreApplication.translate( 'PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}').format( tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) def saveAsScriptFile(self, index=None): tabWidget = self.tabEditorWidget.currentWidget() if not index: index = self.tabEditorWidget.currentIndex() if not tabWidget.path: fileName = self.tabEditorWidget.tabText(index) + '.py' folder = self.settings.value("pythonConsole/lastDirPath", QDir.home()) pathFileName = os.path.join(folder, fileName) fileNone = True else: pathFileName = tabWidget.path fileNone = False saveAsFileTr = QCoreApplication.translate("PythonConsole", "Save File As") filename = QFileDialog.getSaveFileName(self, saveAsFileTr, pathFileName, "Script file (*.py)") if filename: try: tabWidget.save(filename) except (IOError, OSError) as error: msgText = QCoreApplication.translate( 'PythonConsole', 'The file <b>{0}</b> could not be saved. Error: {1}' ).format(tabWidget.path, error.strerror) self.callWidgetMessageBarEditor(msgText, 2, False) if fileNone: tabWidget.path = None else: tabWidget.path = pathFileName return if not fileNone: self.updateTabListScript(pathFileName, action='remove') def openHelp(self): QgsContextHelp.run("PythonConsole") def openSettings(self): if optionsDialog(self).exec_(): self.shell.refreshSettingsShell() self.shellOut.refreshSettingsOutput() self.tabEditorWidget.refreshSettingsEditor() def callWidgetMessageBar(self, text): self.shellOut.widgetMessageBar(iface, text) def callWidgetMessageBarEditor(self, text, level, timed): self.tabEditorWidget.widgetMessageBar(iface, text, level, timed) def updateTabListScript(self, script, action=None): if action == 'remove': self.tabListScript.remove(script) elif action == 'append': if not self.tabListScript: self.tabListScript = [] if script not in self.tabListScript: self.tabListScript.append(script) else: self.tabListScript = [] self.settings.setValue("pythonConsole/tabScripts", self.tabListScript) def saveSettingsConsole(self): self.settings.setValue("pythonConsole/splitterConsole", self.splitter.saveState()) self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState()) self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState()) self.shell.writeHistoryFile(True) def restoreSettingsConsole(self): storedTabScripts = self.settings.value("pythonConsole/tabScripts", []) self.tabListScript = storedTabScripts self.splitter.restoreState( self.settings.value("pythonConsole/splitterConsole", QByteArray())) self.splitterEditor.restoreState( self.settings.value("pythonConsole/splitterEditor", QByteArray())) self.splitterObj.restoreState( self.settings.value("pythonConsole/splitterObj", QByteArray()))
def initialize_widgets(self): """Dynamically set up widgets based on detected files.""" self.widgets_per_file.clear() files_widgets = [ self.widget_general, self.widget_terrain_model, self.widget_simple_infiltration, self.widget_groundwater, self.widget_interflow, ] files_info_collection = [ self.general_files, self.terrain_model_files, self.simple_infiltration_files, self.groundwater_files, self.interflow_files, ] for widget in files_widgets: widget.hide() current_main_layout_row = 1 for widget, files_info in zip(files_widgets, files_info_collection): widget_layout = widget.layout() for field_name, name in files_info.items(): try: file_state = self.detected_files[field_name] except KeyError: continue status = file_state["status"] widget.show() name_label = QLabel(name) name_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) widget_layout.addWidget(name_label, current_main_layout_row, 0) status_label = QLabel(status.value) status_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) widget_layout.addWidget(status_label, current_main_layout_row, 1) empty_label = QLabel() widget_layout.addWidget(empty_label, current_main_layout_row, 2) no_action_pb_name = "Ignore" if status == UploadFileStatus.DELETED_LOCALLY: action_pb_name = "Delete online" else: action_pb_name = "Upload" # Add valid reference widgets all_actions_widget = QWidget() actions_sublayout = QGridLayout() all_actions_widget.setLayout(actions_sublayout) valid_ref_widget = QWidget() valid_ref_sublayout = QGridLayout() valid_ref_widget.setLayout(valid_ref_sublayout) no_action_pb = QPushButton(no_action_pb_name) no_action_pb.setCheckable(True) no_action_pb.setAutoExclusive(True) no_action_pb.clicked.connect( partial(self.toggle_action, field_name, False)) action_pb = QPushButton(action_pb_name) action_pb.setCheckable(True) action_pb.setAutoExclusive(True) action_pb.setChecked(True) action_pb.clicked.connect( partial(self.toggle_action, field_name, True)) valid_ref_sublayout.addWidget(no_action_pb, 0, 0) valid_ref_sublayout.addWidget(action_pb, 0, 1) # Add invalid reference widgets invalid_ref_widget = QWidget() invalid_ref_sublayout = QGridLayout() invalid_ref_widget.setLayout(invalid_ref_sublayout) filepath_sublayout = QGridLayout() filepath_line_edit = QLineEdit() filepath_line_edit.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) browse_pb = QPushButton("...") browse_pb.clicked.connect( partial(self.browse_for_raster, field_name)) filepath_sublayout.addWidget(filepath_line_edit, 0, 0) filepath_sublayout.addWidget(browse_pb, 0, 1) invalid_ref_sublayout.addLayout(filepath_sublayout, 0, 0) update_ref_pb = QPushButton("Update reference") update_ref_pb.clicked.connect( partial(self.update_raster_reference, field_name)) invalid_ref_sublayout.addWidget(update_ref_pb, 0, 1) actions_sublayout.addWidget(valid_ref_widget, 0, 0) actions_sublayout.addWidget(invalid_ref_widget, 0, 1) # Add all actions widget into the main widget layout widget_layout.addWidget(all_actions_widget, current_main_layout_row, 2) # Hide some of the widgets based on files states if status == UploadFileStatus.NO_CHANGES_DETECTED: all_actions_widget.hide() elif status == UploadFileStatus.INVALID_REFERENCE: valid_ref_widget.hide() else: invalid_ref_widget.hide() self.widgets_per_file[field_name] = ( name_label, status_label, valid_ref_widget, action_pb, invalid_ref_widget, filepath_line_edit, ) current_main_layout_row += 1