def getSpinRemoveAreaPixels(wgt, value): sp = QSpinBox(wgt) sp.setRange(0, 1000) sp.setSingleStep(1) sp.setSuffix(' pixels') sp.setValue(value) return sp
def getSpinRemoveAreaPixels(wgt, value): sp = QSpinBox( wgt) sp.setRange(0, 1000) sp.setSingleStep(1) sp.setSuffix(' pixels') sp.setValue(value) return sp
def _create_widget(cls, c, parent, host=None): sb = QSpinBox(parent) sb.setObjectName('{0}_{1}'.format(cls._TYPE_PREFIX, c.name)) # Set ranges c_min = sb.valueFromText(str(c.minimum)) c_max = sb.valueFromText(str(c.maximum)) sb.setMinimum(c.minimum) sb.setMaximum(c.maximum) return sb
def valueWidget(self, field, data): """ Retrieves correct widget for a given field based on its type. :param field: (QgsField) field to be represented. :param data: (float/int/str/bool) initial data to be set to widget. :return: (QDoubleSpinBox/QSpinBox/QLineEdit/QCheckBox) the adequate widget for field. """ if utils.fieldIsBool(field): # vWidget = QCheckBox() vWidget = self.centeredCheckBox() if not data is None: vWidget.cb.setChecked(data) elif utils.fieldIsFloat(field): vWidget = QDoubleSpinBox() vWidget.setMaximum(99999999) vWidget.setMinimum(-99999999) if data is not None: vWidget.setValue(data) elif utils.fieldIsInt(field): vWidget = QSpinBox() vWidget.setMaximum(99999999) vWidget.setMinimum(-99999999) if data is not None: vWidget.setValue(data) else: vWidget = QLineEdit() vWidget.setPlaceholderText( self.tr("Type the value for {0}").format(field.name())) if data is not None: vWidget.setText(data) return vWidget
def setupFields(self): """ Setups up all fields and fill up with available data on the attribute map. """ utils = Utils() row = 0 # in case no fields are provided for row, f in enumerate(self.fields): fName = f.name() fMap = self.attributeMap[fName] if fName in self.attributeMap \ else None if fName in self.attributeMap: fMap = self.attributeMap[fName] if fMap["ignored"]: w = QLineEdit() w.setText(self.tr("Field is set to be ignored")) value = None enabled = False else: value = fMap["value"] enabled = fMap["editable"] if fMap["isPk"]: # visually identify primary key attributes text = '<p>{0} <img src=":/plugins/DsgTools/icons/key.png" '\ 'width="16" height="16"></p>'.format(fName) else: text = fName else: value = None enabled = True text = fName if fName in self.attributeMap and self.attributeMap[fName][ "ignored"]: pass elif utils.fieldIsFloat(f): w = QDoubleSpinBox() w.setValue(0 if value is None else value) elif utils.fieldIsInt(f): w = QSpinBox() w.setValue(0 if value is None else value) else: w = QLineEdit() w.setText("" if value is None else value) w.setEnabled(enabled) # also to make easier to read data self._fieldsWidgets[fName] = w label = QLabel(text) label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) w.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.widgetsLayout.addWidget(label, row, 0) self.widgetsLayout.addWidget(w, row, 1) self.widgetsLayout.addItem( QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Expanding), row + 1, 1, 1, 2) # row, col, rowSpan, colSpan
def extra_keywords_to_widgets(extra_keyword_definition): """Create widgets for extra keyword. :param extra_keyword_definition: An extra keyword definition. :type extra_keyword_definition: dict :return: QCheckBox and The input widget :rtype: (QCheckBox, QWidget) """ # Check box check_box = QCheckBox(extra_keyword_definition['name']) check_box.setToolTip(extra_keyword_definition['description']) check_box.setChecked(True) # Input widget if extra_keyword_definition['type'] == float: input_widget = QDoubleSpinBox() input_widget.setMinimum(extra_keyword_definition['minimum']) input_widget.setMaximum(extra_keyword_definition['maximum']) input_widget.setSuffix(extra_keyword_definition['unit_string']) elif extra_keyword_definition['type'] == int: input_widget = QSpinBox() input_widget.setMinimum(extra_keyword_definition['minimum']) input_widget.setMaximum(extra_keyword_definition['maximum']) input_widget.setSuffix(extra_keyword_definition['unit_string']) elif extra_keyword_definition['type'] == str: if extra_keyword_definition.get('options'): input_widget = QComboBox() options = extra_keyword_definition['options'] for option in options: input_widget.addItem( option['name'], option['key'], ) default_option_index = input_widget.findData( extra_keyword_definition['default_option']) input_widget.setCurrentIndex(default_option_index) else: input_widget = QLineEdit() elif extra_keyword_definition['type'] == datetime: input_widget = QDateTimeEdit() input_widget.setCalendarPopup(True) input_widget.setDisplayFormat('hh:mm:ss, d MMM yyyy') input_widget.setDateTime(datetime.now()) else: raise Exception input_widget.setToolTip(extra_keyword_definition['description']) # Signal # noinspection PyUnresolvedReferences check_box.stateChanged.connect(input_widget.setEnabled) return check_box, input_widget
def construct_form_param_system(self, row, pos): widget = None for field in row[pos]['fields']: if field['label']: lbl = QLabel() lbl.setObjectName('lbl' + field['widgetname']) lbl.setText(field['label']) lbl.setMinimumSize(160, 0) lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) lbl.setToolTip(field['tooltip']) if field['widgettype'] == 'text' or field[ 'widgettype'] == 'linetext': widget = QLineEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'textarea': widget = QTextEdit() widget.setText(field['value']) widget.editingFinished.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'combo': widget = QComboBox() self.populate_combo(widget, field) widget.currentIndexChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'checkbox' or field[ 'widgettype'] == 'check': widget = QCheckBox() if field['value'] in ('true', 'True', 'TRUE', True): widget.setChecked(True) elif field['value'] in ('false', 'False', 'FALSE', False): widget.setChecked(False) widget.stateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) elif field['widgettype'] == 'datetime': widget = QDateEdit() widget.setCalendarPopup(True) if field['value']: field['value'] = field['value'].replace('/', '-') date = QDate.fromString(field['value'], 'yyyy-MM-dd') widget.setDate(date) widget.dateChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) elif field['widgettype'] == 'spinbox': widget = QSpinBox() if 'value' in field and field['value'] is not None: value = float(str(field['value'])) widget.setValue(value) widget.valueChanged.connect( partial(self.get_values_changed_param_system, widget)) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) else: pass if widget: widget.setObjectName(field['widgetname']) else: pass # Order Widgets if 'layoutname' in field: if field['layoutname'] == 'lyt_topology': self.order_widgets_system(field, self.topology_form, lbl, widget) elif field['layoutname'] == 'lyt_builder': self.order_widgets_system(field, self.builder_form, lbl, widget) elif field['layoutname'] == 'lyt_review': self.order_widgets_system(field, self.review_form, lbl, widget) elif field['layoutname'] == 'lyt_analysis': self.order_widgets_system(field, self.analysis_form, lbl, widget) elif field['layoutname'] == 'lyt_system': self.order_widgets_system(field, self.system_form, lbl, widget)
class Slider(InputType): ''' slider input, displays a slider and a number input next to it, both connected to each other ''' def __init__(self, minimum: int = 0, maximum: int = 100000000, step: int = 1, width: int = 300, lockable: bool = False, locked: bool = False, **kwargs): ''' Parameters ---------- width : int, optional width of slider in pixels, defaults to 300 pixels minimum : int, optional minimum value that the user can set maximum : int, optional maximum value that the user can set step : int, optional the tick intervall of the slider and single step of the number input, defaults to 1 lockable : bool, optional the slider and number input can be locked by a checkbox that will be displayed next to them if True, defaults to not lockable locked : bool, optional initial lock-state of inputs, only applied if lockable is True, defaults to inputs being not locked ''' super().__init__(**kwargs) self.minimum = minimum self.maximum = maximum self.lockable = lockable self.step = step self.slider = QSlider(Qt.Horizontal) self.slider.setMinimum(minimum) self.slider.setMaximum(maximum) self.slider.setTickInterval(step) self.slider.setFixedWidth(width) self.spinbox = QSpinBox() self.spinbox.setMinimum(minimum) self.spinbox.setMaximum(maximum) self.spinbox.setSingleStep(step) self.registerFocusEvent(self.spinbox) self.registerFocusEvent(self.slider) if lockable: self.lock_button = QPushButton() self.lock_button.setCheckable(True) self.lock_button.setChecked(locked) self.lock_button.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed) def toggle_icon(emit=True): is_locked = self.lock_button.isChecked() fn = '20190619_iconset_mob_lock_locked_02.png' if is_locked \ else '20190619_iconset_mob_lock_unlocked_03.png' self.slider.setEnabled(not is_locked) self.spinbox.setEnabled(not is_locked) icon_path = os.path.join(settings.IMAGE_PATH, 'iconset_mob', fn) icon = QIcon(icon_path) self.lock_button.setIcon(icon) self.locked.emit(is_locked) toggle_icon(emit=False) self.lock_button.clicked.connect(lambda: toggle_icon(emit=True)) self.slider.valueChanged.connect( lambda: self.set_value(self.slider.value())) self.spinbox.valueChanged.connect( lambda: self.set_value(self.spinbox.value())) self.slider.valueChanged.connect( lambda: self.changed.emit(self.get_value())) self.spinbox.valueChanged.connect( lambda: self.changed.emit(self.get_value()) ) def set_value(self, value: int): ''' set a number to both the slider and the number input Parameters ---------- checked : int check-state ''' for element in [self.slider, self.spinbox]: # avoid infinite recursion element.blockSignals(True) element.setValue(value or 0) element.blockSignals(False) @property def is_locked(self) -> bool: ''' Returns ------- bool current lock-state of slider and number input ''' if not self.lockable: return False return self.lock_button.isChecked() def draw(self, layout: QLayout, unit: str = ''): ''' add slider, the connected number and the lock (if lockable) input to the layout Parameters ---------- layout : QLayout layout to add the inputs to unit : str, optional the unit shown after the value, defaults to no unit ''' l = QHBoxLayout() l.addWidget(self.slider) l.addWidget(self.spinbox) if unit: l.addWidget(QLabel(unit)) if self.lockable: l.addWidget(self.lock_button) layout.addLayout(l) def get_value(self) -> int: ''' get the currently set number Returns ------- int currently set number ''' return self.slider.value()
def __init__(self, minimum: int = 0, maximum: int = 100000000, step: int = 1, width: int = 300, lockable: bool = False, locked: bool = False, **kwargs): ''' Parameters ---------- width : int, optional width of slider in pixels, defaults to 300 pixels minimum : int, optional minimum value that the user can set maximum : int, optional maximum value that the user can set step : int, optional the tick intervall of the slider and single step of the number input, defaults to 1 lockable : bool, optional the slider and number input can be locked by a checkbox that will be displayed next to them if True, defaults to not lockable locked : bool, optional initial lock-state of inputs, only applied if lockable is True, defaults to inputs being not locked ''' super().__init__(**kwargs) self.minimum = minimum self.maximum = maximum self.lockable = lockable self.step = step self.slider = QSlider(Qt.Horizontal) self.slider.setMinimum(minimum) self.slider.setMaximum(maximum) self.slider.setTickInterval(step) self.slider.setFixedWidth(width) self.spinbox = QSpinBox() self.spinbox.setMinimum(minimum) self.spinbox.setMaximum(maximum) self.spinbox.setSingleStep(step) self.registerFocusEvent(self.spinbox) self.registerFocusEvent(self.slider) if lockable: self.lock_button = QPushButton() self.lock_button.setCheckable(True) self.lock_button.setChecked(locked) self.lock_button.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed) def toggle_icon(emit=True): is_locked = self.lock_button.isChecked() fn = '20190619_iconset_mob_lock_locked_02.png' if is_locked \ else '20190619_iconset_mob_lock_unlocked_03.png' self.slider.setEnabled(not is_locked) self.spinbox.setEnabled(not is_locked) icon_path = os.path.join(settings.IMAGE_PATH, 'iconset_mob', fn) icon = QIcon(icon_path) self.lock_button.setIcon(icon) self.locked.emit(is_locked) toggle_icon(emit=False) self.lock_button.clicked.connect(lambda: toggle_icon(emit=True)) self.slider.valueChanged.connect( lambda: self.set_value(self.slider.value())) self.spinbox.valueChanged.connect( lambda: self.set_value(self.spinbox.value())) self.slider.valueChanged.connect( lambda: self.changed.emit(self.get_value())) self.spinbox.valueChanged.connect( lambda: self.changed.emit(self.get_value()) )
class QvFormSimbMapificacio(QvFormBaseMapificacio): def __init__(self, llegenda, capa=None, amplada=500): super().__init__(llegenda, amplada) if capa is None: self.capa = llegenda.currentLayer() else: self.capa = capa self.info = None if not self.iniParams(): return self.setWindowTitle('Modificar mapa simbòlic ' + self.renderParams.tipusMapa.lower()) self.layout = QVBoxLayout() self.layout.setSpacing(14) self.setLayout(self.layout) self.color = QComboBox(self) self.color.setEditable(False) self.comboColors(self.color) self.contorn = QComboBox(self) self.contorn.setEditable(False) self.comboColors(self.contorn, mv.MAP_CONTORNS) self.color.currentIndexChanged.connect(self.canviaContorns) self.metode = QComboBox(self) self.metode.setEditable(False) if self.renderParams.numCategories > 1: self.metode.addItems(mv.MAP_METODES_MODIF.keys()) else: self.metode.addItems(mv.MAP_METODES.keys()) self.metode.setCurrentIndex(-1) self.metode.currentIndexChanged.connect(self.canviaMetode) self.nomIntervals = QLabel("Nombre d'intervals:", self) self.intervals = QSpinBox(self) self.intervals.setMinimum(min(2, self.renderParams.numCategories)) self.intervals.setMaximum(max(mv.MAP_MAX_CATEGORIES, self.renderParams.numCategories)) self.intervals.setSingleStep(1) self.intervals.setValue(4) if self.renderParams.tipusMapa == 'Àrees': self.intervals.setSuffix(" (depèn del mètode)") # self.intervals.valueChanged.connect(self.deselectValue) self.nomTamany = QLabel("Tamany cercle:", self) self.tamany = QSpinBox(self) self.tamany.setMinimum(1) self.tamany.setMaximum(12) self.tamany.setSingleStep(1) self.tamany.setValue(4) self.bInfo = QPushButton('Info') self.bInfo.clicked.connect(self.veureInfo) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.cancel) self.buttons.addButton(self.bInfo, QDialogButtonBox.ResetRole) self.gSimb = QGroupBox('Simbologia del mapa') self.lSimb = QFormLayout() self.lSimb.setSpacing(14) self.gSimb.setLayout(self.lSimb) self.lSimb.addRow('Color base:', self.color) self.lSimb.addRow('Color contorn:', self.contorn) if self.renderParams.tipusMapa == 'Àrees': self.lSimb.addRow('Mètode classificació:', self.metode) self.lSimb.addRow(self.nomIntervals, self.intervals) self.nomTamany.setVisible(False) self.tamany.setVisible(False) else: self.metode.setVisible(False) self.nomIntervals.setVisible(False) self.intervals.setVisible(False) self.lSimb.addRow(self.nomTamany, self.tamany) self.wInterval = [] for w in self.iniIntervals(): self.wInterval.append(w) self.gInter = self.grupIntervals() self.layout.addWidget(self.gSimb) if self.renderParams.tipusMapa == 'Àrees': self.layout.addWidget(self.gInter) self.layout.addWidget(self.buttons) self.valorsInicials() def iniParams(self): self.info = QgsExpressionContextUtils.layerScope(self.capa).variable(mv.MAP_ID) if self.info is None: return False self.renderParams = QvMapRendererParams.fromLayer(self.capa) if self.renderParams.msgError == '': self.custom = (self.renderParams.modeCategories == 'Personalitzat') return True else: self.msgInfo("No s'han pogut recuperar els paràmetres del mapa simbòlic\n\n" + "Error: " + self.renderParams.msgError) return False @pyqtSlot() def veureInfo(self): if self.info is not None: box = QMessageBox(self) box.setWindowTitle('Info del mapa simbòlic') txt = '<table width="600">' params = self.info.split('\n') for param in params: linea = param.strip() if linea.endswith(':'): linea += ' ---' txt += '<tr><td><nobr>· {}</nobr></td></tr>'.format(linea) txt += '</table>' box.setTextFormat(Qt.RichText) box.setText("Paràmetres d'agregació de dades:") box.setInformativeText(txt) box.setIcon(QMessageBox.Information) box.setStandardButtons(QMessageBox.Ok) box.setDefaultButton(QMessageBox.Ok) box.exec() def valorsInicials(self): self.color.setCurrentIndex(self.color.findText(self.renderParams.colorBase)) self.contorn.setCurrentIndex(self.contorn.findText(self.renderParams.colorContorn)) self.intervals.setValue(self.renderParams.numCategories) self.tamany.setValue(self.renderParams.increase) self.metode.setCurrentIndex(self.metode.findText(self.renderParams.modeCategories)) def valorsFinals(self): self.renderParams.colorBase = self.color.currentText() self.renderParams.colorContorn = self.contorn.currentText() self.renderParams.modeCategories = self.metode.currentText() self.renderParams.numCategories = self.intervals.value() self.renderParams.increase = self.tamany.value() if self.custom: self.renderParams.rangsCategories = [] for fila in self.wInterval: self.renderParams.rangsCategories.append((fila[0].text(), fila[2].text())) self.renderParams.numCategories = len(self.renderParams.rangsCategories) def txtRang(self, num): if type(num) == str: return num return QvApp().locale.toString(num, 'f', self.renderParams.numDecimals) def iniFilaInterval(self, iniValor, finValor): maxSizeB = 27 # validator = QDoubleValidator(self) # validator.setLocale(QvApp().locale) # validator.setNotation(QDoubleValidator.StandardNotation) # validator.setDecimals(5) validator = QvVerifNumero(self) ini = QLineEdit(self) ini.setText(self.txtRang(iniValor)) ini.setValidator(validator) sep = QLabel('-', self) fin = QLineEdit(self) fin.setText(self.txtRang(finValor)) fin.setValidator(validator) fin.editingFinished.connect(self.nouTall) add = QPushButton('+', self) add.setMaximumSize(maxSizeB, maxSizeB) add.setToolTip('Afegeix nou interval') add.clicked.connect(self.afegirFila) add.setFocusPolicy(Qt.NoFocus) rem = QPushButton('-', self) rem.setMaximumSize(maxSizeB, maxSizeB) rem.setToolTip('Esborra interval') rem.clicked.connect(self.eliminarFila) rem.setFocusPolicy(Qt.NoFocus) return [ini, sep, fin, add, rem] def iniIntervals(self): for cat in self.renderParams.rangsCategories: yield self.iniFilaInterval(cat.lowerValue(), cat.upperValue()) def grupIntervals(self): group = QGroupBox('Definició dels intervals') # group.setMinimumWidth(400) layout = QGridLayout() layout.setSpacing(10) # layout.setColumnMinimumWidth(4, 40) numFilas = len(self.wInterval) for fila, widgets in enumerate(self.wInterval): for col, w in enumerate(widgets): # Primera fila: solo + if fila == 0 and col > 3: w.setVisible(False) # # Ultima fila: no hay + ni - elif fila > 0 and fila == (numFilas - 1) and col > 2: w.setVisible(False) else: w.setVisible(True) # Valor inicial deshabilitado (menos 1a fila) if col == 0 and fila != 0: w.setDisabled(True) w.setProperty('Fila', fila) layout.addWidget(w, fila, col) group.setLayout(layout) return group def actGrupIntervals(self): self.intervals.setValue(len(self.wInterval)) self.setUpdatesEnabled(False) self.buttons.setVisible(False) self.gInter.setVisible(False) self.layout.removeWidget(self.buttons) self.layout.removeWidget(self.gInter) self.gInter.deleteLater() self.gInter = self.grupIntervals() self.layout.addWidget(self.gInter) self.layout.addWidget(self.buttons) self.gInter.setVisible(True) self.buttons.setVisible(True) self.adjustSize() self.setUpdatesEnabled(True) @pyqtSlot() def afegirFila(self): masFilas = (len(self.wInterval) < mv.MAP_MAX_CATEGORIES) if masFilas: f = self.sender().property('Fila') + 1 ini = self.wInterval[f][0] val = ini.text() ini.setText('') w = self.iniFilaInterval(val, '') self.wInterval.insert(f, w) self.actGrupIntervals() self.wInterval[f][2].setFocus() else: self.msgInfo("S'ha arribat al màxim d'intervals possibles") @pyqtSlot() def eliminarFila(self): f = self.sender().property('Fila') ini = self.wInterval[f][0] val = ini.text() del self.wInterval[f] ini = self.wInterval[f][0] ini.setText(val) self.actGrupIntervals() @pyqtSlot() def nouTall(self): w = self.sender() if w.isModified(): f = w.property('Fila') + 1 if f < len(self.wInterval): ini = self.wInterval[f][0] ini.setText(w.text()) w.setModified(False) @pyqtSlot() def canviaMetode(self): self.custom = (self.metode.currentText() == 'Personalitzat') if self.custom: self.intervals.setValue(len(self.wInterval)) self.intervals.setEnabled(not self.custom) self.gInter.setVisible(self.custom) self.adjustSize() # print('GSIMB -> Ancho:', self.gSimb.size().width(), '- Alto:', self.gSimb.size().height()) # print('FORM -> Ancho:', self.size().width(), '- Alto:', self.size().height()) @pyqtSlot() def canviaContorns(self): self.comboColors(self.contorn, mv.MAP_CONTORNS, mv.MAP_COLORS[self.color.currentText()], True) def leSelectFocus(self, wLineEdit): lon = len(wLineEdit.text()) if lon > 0: wLineEdit.setSelection(0, lon) wLineEdit.setFocus() def validaNum(self, wLineEdit): val = wLineEdit.validator() if val is None: return True res = val.validate(wLineEdit.text(), 0) if res[0] == QValidator.Acceptable: return True else: self.msgInfo("Cal introduir un nombre enter o amb decimals.\n" "Es farà servir la coma (,) per separar els decimals.\n" "I pels milers, opcionalment, el punt (.)") self.leSelectFocus(wLineEdit) return False def validaInterval(self, wLineEdit1, wLineEdit2): num1, _ = QvApp().locale.toFloat(wLineEdit1.text()) num2, _ = QvApp().locale.toFloat(wLineEdit2.text()) if num2 >= num1: return True else: self.msgInfo("El segon nombre de l'interval ha de ser major que el primer") self.leSelectFocus(wLineEdit2) return False def validaFila(self, fila): wLineEdit1 = fila[0] wLineEdit2 = fila[2] if not self.validaNum(wLineEdit1): return False if not self.validaNum(wLineEdit2): return False if not self.validaInterval(wLineEdit1, wLineEdit2): return False return True def valida(self): if self.custom: for fila in self.wInterval: if not self.validaFila(fila): return False return True def procesa(self): self.valorsFinals() try: mapRenderer = self.renderParams.mapRenderer(self.llegenda) if self.custom: self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase] self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn] self.renderer = mapRenderer.customRender(self.capa) else: self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase] if self.renderParams.colorContorn == 'Base': self.renderParams.colorContorn = self.renderParams.colorBase else: self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn] self.renderParams.modeCategories = \ mv.MAP_METODES_MODIF[self.renderParams.modeCategories] self.renderer = mapRenderer.calcRender(self.capa) if self.renderer is None: return "No s'ha pogut elaborar el mapa simbòlic" err = self.llegenda.saveStyleToGeoPackage(self.capa, mv.MAP_ID) if err != '': return "Hi ha hagut problemes al desar la simbologia\n({})".format(err) # self.llegenda.modificacioProjecte('mapModified') return '' except Exception as e: return "No s'ha pogut modificar el mapa simbòlic\n({})".format(str(e))
def __init__(self, llegenda, capa=None, amplada=500): super().__init__(llegenda, amplada) if capa is None: self.capa = llegenda.currentLayer() else: self.capa = capa self.info = None if not self.iniParams(): return self.setWindowTitle('Modificar mapa simbòlic ' + self.renderParams.tipusMapa.lower()) self.layout = QVBoxLayout() self.layout.setSpacing(14) self.setLayout(self.layout) self.color = QComboBox(self) self.color.setEditable(False) self.comboColors(self.color) self.contorn = QComboBox(self) self.contorn.setEditable(False) self.comboColors(self.contorn, mv.MAP_CONTORNS) self.color.currentIndexChanged.connect(self.canviaContorns) self.metode = QComboBox(self) self.metode.setEditable(False) if self.renderParams.numCategories > 1: self.metode.addItems(mv.MAP_METODES_MODIF.keys()) else: self.metode.addItems(mv.MAP_METODES.keys()) self.metode.setCurrentIndex(-1) self.metode.currentIndexChanged.connect(self.canviaMetode) self.nomIntervals = QLabel("Nombre d'intervals:", self) self.intervals = QSpinBox(self) self.intervals.setMinimum(min(2, self.renderParams.numCategories)) self.intervals.setMaximum(max(mv.MAP_MAX_CATEGORIES, self.renderParams.numCategories)) self.intervals.setSingleStep(1) self.intervals.setValue(4) if self.renderParams.tipusMapa == 'Àrees': self.intervals.setSuffix(" (depèn del mètode)") # self.intervals.valueChanged.connect(self.deselectValue) self.nomTamany = QLabel("Tamany cercle:", self) self.tamany = QSpinBox(self) self.tamany.setMinimum(1) self.tamany.setMaximum(12) self.tamany.setSingleStep(1) self.tamany.setValue(4) self.bInfo = QPushButton('Info') self.bInfo.clicked.connect(self.veureInfo) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.cancel) self.buttons.addButton(self.bInfo, QDialogButtonBox.ResetRole) self.gSimb = QGroupBox('Simbologia del mapa') self.lSimb = QFormLayout() self.lSimb.setSpacing(14) self.gSimb.setLayout(self.lSimb) self.lSimb.addRow('Color base:', self.color) self.lSimb.addRow('Color contorn:', self.contorn) if self.renderParams.tipusMapa == 'Àrees': self.lSimb.addRow('Mètode classificació:', self.metode) self.lSimb.addRow(self.nomIntervals, self.intervals) self.nomTamany.setVisible(False) self.tamany.setVisible(False) else: self.metode.setVisible(False) self.nomIntervals.setVisible(False) self.intervals.setVisible(False) self.lSimb.addRow(self.nomTamany, self.tamany) self.wInterval = [] for w in self.iniIntervals(): self.wInterval.append(w) self.gInter = self.grupIntervals() self.layout.addWidget(self.gSimb) if self.renderParams.tipusMapa == 'Àrees': self.layout.addWidget(self.gInter) self.layout.addWidget(self.buttons) self.valorsInicials()
def getSpinBoxAzimuth(wgt, value): sp = QSpinBox(wgt) sp.setRange(0, 45) sp.setSingleStep(1) sp.setSuffix(' degrees') sp.setValue(value) msg = QCoreApplication.translate( 'GimpSelectionFeature', 'Degrees of azimuth between vertexs') sp.setToolTip(msg) return sp
def createWidget(self): self.scrollwidget = QtWidgets.QScrollArea() self.scrollwidget.setWidgetResizable(True) self.tabs = QtWidgets.QTabWidget() self.scrollwidget.setWidget(self.tabs) self.names = [ 'Settings', 'DTM', 'Slope', '2D-Approximation', 'Topologic correction', 'Editing', '3D-Modelling', 'Editing (3D)', 'Export' ] self.widgets = {} self.settings = {} self.modules = {} for idx, name in enumerate(self.names): self.widgets[name] = QtWidgets.QDialog() ls = QtWidgets.QFormLayout() # Tab-specific options if name == "Settings": desc = QtWidgets.QLabel( "Welcome to the qpals LineModeler GUI! \nThis tool will help you to detect and " "model breaklines based on a DTM and/or a point cloud using the opals module " "opalsLineModeler.\nThe process includes manual editing in QGIS (\"Editing\") " "as well as automatic dectection and modelling.\n\n" "To begin, please enter some basic information.") desc.setWordWrap(True) ls.addRow(desc) boxRun = QtWidgets.QGroupBox( "Run multiple steps automatically:") boxVL = QtWidgets.QVBoxLayout() boxRun.setLayout(boxVL) self.settings['settings'] = OrderedDict([ ('name', QtWidgets.QLineEdit()), ('inFile', QpalsDropTextbox.QpalsDropTextbox( layerlist=self.layerlist)), ('tempFolder', QpalsDropTextbox.QpalsDropTextbox()), ('outFolder', QpalsDropTextbox.QpalsDropTextbox()), ('chkDTM', QtWidgets.QCheckBox("DTM")), ('chkSlope', QtWidgets.QCheckBox("Slope")), ('chk2D', QtWidgets.QCheckBox("2D-Approximation")), ('chktopo2D', QtWidgets.QCheckBox("Topological correction")), ('chkEditing2d', QtWidgets.QLabel( "--- Manual editing of 2D-Approximations ---")), ('chk3Dmodel', QtWidgets.QCheckBox("3D-Modelling")), ('chkEditing3d', QtWidgets.QLabel("--- Manual editing of 3D-Lines ---")), ('chkExport', QtWidgets.QCheckBox("Export")), ]) for key, value in list(self.settings['settings'].items()): if isinstance(value, QpalsDropTextbox.QpalsDropTextbox): value.setMinimumContentsLength(20) value.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToMinimumContentsLength) if key.startswith("chk"): boxVL.addWidget(value) ls.addRow(QtWidgets.QLabel("Project name"), self.settings['settings']['name']) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['inFile'], stretch=1) ls.addRow(QtWidgets.QLabel("Input file (TIFF/LAS/ODM)"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['tempFolder'], stretch=1) self.settings['settings']['tempFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") ls.addRow(QtWidgets.QLabel("Folder for temporary files"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() self.settings['settings']['outFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") hbox_wrap.addWidget(self.settings['settings']['outFolder'], stretch=1) ls.addRow(QtWidgets.QLabel("Folder for output files"), hbox_wrap) ls.addRow(QtWidgets.QLabel("")) boxBtnRun = QtWidgets.QPushButton("Run selected steps now") boxBtnRun.clicked.connect(lambda: self.run_step("all")) boxBtnExp = QtWidgets.QPushButton( "Export selected steps to .bat") boxBtnExp.clicked.connect(self.createBatFile) # saveBtn = QtWidgets.QPushButton("Save to project file") # saveBtn.clicked.connect(self.save) boxVL.addWidget(boxBtnRun) boxVL.addWidget(boxBtnExp) # boxVL.addWidget(saveBtn) ls.addRow(boxRun) if name == "DTM": desc = QtWidgets.QLabel( "This first step will create a digital terrain model (DTM) from your point cloud data. " "Also, a shading of your DTM " "will be created for visualisation purposes. If the input file is not an ODM, one has to be " "created for the modelling process later on.") desc.setWordWrap(True) ls.addRow(desc) impmod, impscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsImport", "opalsImport", self.project, {'outFile': 'pointcloud.odm'}, ["inFile", "outFile"]) self.modules['dtmImp'] = impmod self.widgets['dtmImp'] = impscroll ls.addRow(impscroll) dtmmod, dtmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGrid", "opalsGrid", self.project, { 'interpolation': 'movingPlanes', 'gridSize': '1', 'outFile': 'DTM_1m.tif' }, [ "inFile", "outFile", "neighbours", "searchRadius", "interpolation" ]) self.modules['dtmGrid'] = dtmmod self.widgets['dtmGrid'] = dtmscroll dtmmod.afterRun = self.addDtm ls.addRow(dtmscroll) shdmod, shdscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsShade", "opalsShade", self.project, { 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_shd.tif' }, [ "inFile", "outFile", ]) self.modules['dtmShade'] = shdmod shdmod.afterRun = self.addShd ls.addRow(shdscroll) if name == "Slope": desc = QtWidgets.QLabel( "To automatically detect breaklines, a slope map is calculated. This map uses the neighboring 9" " pixels to estimate a plane. The gradient (steepest slope) is then taken, converted to a slope" "in degrees, and assigned to the pixel.") desc.setWordWrap(True) ls.addRow(desc) gfmod, gfscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGridFeature", "opalsGridFeature", self.project, { 'feature': 'slpDeg', 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_slope.tif' }, ["inFile", "outFile", "feature"]) self.modules['slope'] = gfmod ls.addRow(gfscroll) if name == "2D-Approximation": desc = QtWidgets.QLabel( "The slope map is used to detect breaklines. For this, the algorithm by Canny (1986) is used.\n" "First, the slope map is convoluted with a gaussian kernel for smoothing, then the derivative " "is calculated. The two threshold parameters represent the upper and lower values for the " "binarization of the derivative map. Edges that have at least one pixel > upper threshold will be " "followed until they have a pixel < lower threshold.") desc.setWordWrap(True) ls.addRow(desc) edgeDmod, edgeDscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsEdgeDetect", "opalsEdgeDetect", self.project, { 'threshold': '2;4', 'sigmaSmooth': '1.8', 'inFile': 'DTM_1m_slope_slpDeg.tif', 'outFile': 'detected_edges.tif' }, ["inFile", "outFile", "threshold", "sigmaSmooth"]) self.modules['edgeDetect'] = edgeDmod ls.addRow(edgeDscroll) desc = QtWidgets.QLabel( "Since the output of opalsEdgeDetect is still a raster, we need to vectorize it:" ) desc.setWordWrap(True) ls.addRow(desc) vecmod, vecscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsVectorize", "opalsVectorize", self.project, { 'inFile': 'detected_edges.tif', 'outFile': 'detected_edges.shp' }, ["inFile", "outFile"]) self.modules['vectorize'] = vecmod ls.addRow(vecscroll) if name == "Topologic correction": desc = QtWidgets.QLabel( "Vectorized binary rasters usually need some topological cleaning. Here, this is done in three steps: \n" "1) Find the longest line and remove all lines < 10m\n" "2) Merge lines iteratively\n" "3) Clean up") desc.setWordWrap(True) ls.addRow(desc) lt1mod, lt1scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (1)", self.project, { 'method': 'longest', 'minLength': '10', 'snapRadius': '0', 'maxTol': '0.5', 'maxAngleDev': '75;15', 'avgDist': '3', 'inFile': 'detected_edges.shp', 'outFile': 'edges1.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt1'] = lt1mod ls.addRow(lt1scroll) lt2mod, lt2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (2)", self.project, { 'method': 'merge', 'minLength': '10', 'snapRadius': '3', 'maxTol': '0', 'maxAngleDev': '150;15', 'avgDist': '3', 'merge.minWeight': '0.75', 'merge.relWeightLead': '0', 'merge.maxIter': '10', 'merge.revertDist': '5', 'merge.revertInterval': '1', 'merge.searchGeneration': '4', 'merge.preventIntersection': '1', 'inFile': 'edges1.shp', 'outFile': 'edges2.shp' }, [ "inFile", "outFile", "method", "maxAngleDev", "snapRadius", "merge\..*" ]) lt2scroll.setFixedHeight(lt2scroll.height() - 200) self.modules['lt2'] = lt2mod ls.addRow(lt2scroll) lt3mod, lt3scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (3)", self.project, { 'method': 'longest', 'minLength': '25', 'snapRadius': '0', 'maxTol': '0', 'maxAngleDev': '90;15', 'avgDist': '3', 'inFile': 'edges2.shp', 'outFile': 'edges3.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt3'] = lt3mod ls.addRow(lt3scroll) lt3mod.afterRun = self.add2DLines if name == "Editing": desc = QtWidgets.QLabel( "Please start editing the 2D approximations that have been loaded into qgis. Here are some tools " "that might help:") desc.setWordWrap(True) ls.addRow(desc) box1 = QtWidgets.QGroupBox("QuickLineModeller") from . import QpalsQuickLM self.quicklm = QpalsQuickLM.QpalsQuickLM( project=self.project, layerlist=self.layerlist, iface=self.iface) box1.setLayout(self.quicklm.fl) ls.addRow(box1) box2 = QtWidgets.QGroupBox("qpalsSection") from . import QpalsSection self.section = QpalsSection.QpalsSection( project=self.project, layerlist=self.layerlist, iface=self.iface) self.section.createWidget() box2.setLayout(self.section.ls) box2.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) ls.addRow(box2) if name == "3D-Modelling": desc = QtWidgets.QLabel( "The 2D approximations can now be used to model 3D breaklines in the pointcloud/the DTM." ) desc.setWordWrap(True) ls.addRow(desc) lmmod, lmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineModeler", "opalsLineModeler", self.project, { #"filter": "Class[Ground]", "approxFile": "edges3.shp", "outFile": "modelled_lines.shp" }, [ "inFile", "approxFile", "outFile", "filter", "patchLength", "patchWidth", "overlap", "angle", "minLength", "pointCount", "sigmaApriori" ]) self.modules['lm'] = lmmod ls.addRow(lmscroll) lmmod.afterRun = self.add3DLines if name == "Editing (3D)": desc = QtWidgets.QLabel( "Before exporting the final product, there are a few tools to check the " "quality of the result. This includes a topological check as well as a search" "for points that have a big height difference to the DTM - and might be erraneous." ) desc.setWordWrap(True) ls.addRow(desc) self.startQualityCheckBtn = QtWidgets.QPushButton( "Start calculation") self.startQualityCheckBtn.clicked.connect( self.runProblemSearchAsync) self.QualityCheckbar = QtWidgets.QProgressBar() self.QualityCheckDtm = QgsMapLayerComboBox() self.QualityCheckDtm.setFilters( QgsMapLayerProxyModel.RasterLayer) self.QualityCheckThreshold = QtWidgets.QLineEdit("0.5") ls.addRow( QtWidgets.QLabel("DTM Layer to compare heights with"), self.QualityCheckDtm) ls.addRow( QtWidgets.QLabel("Set height difference threshold [m]"), self.QualityCheckThreshold) hb = QtWidgets.QHBoxLayout() hb.addWidget(self.QualityCheckbar) hb.addWidget(self.startQualityCheckBtn) ls.addRow(hb) line = QtWidgets.QFrame() line.setFrameShape(QtWidgets.QFrame.HLine) line.setFrameShadow(QtWidgets.QFrame.Sunken) ls.addRow(line) self.editingls = ls self.edit3d_linelayerbox = QgsMapLayerComboBox() self.edit3d_linelayerbox.setFilters( QgsMapLayerProxyModel.LineLayer) self.edit3d_pointlayerbox = QgsMapLayerComboBox() self.edit3d_pointlayerbox.setFilters( QgsMapLayerProxyModel.PointLayer) self.edit3d_dtmlayerbox = QgsMapLayerComboBox() self.edit3d_dtmlayerbox.setFilters( QgsMapLayerProxyModel.RasterLayer) self.edit3d_pointlayerbox.currentIndexChanged.connect( self.nodeLayerChanged) self.edit3d_currPointId = QSpinBox() self.edit3d_currPointId.setMinimum(0) self.edit3d_currPointId.valueChanged.connect( self.showProblemPoint) ls.addRow("Select Line Layer:", self.edit3d_linelayerbox) ls.addRow("Select Problem Point layer:", self.edit3d_pointlayerbox) self.selectNodeBtn = QtWidgets.QPushButton("Next point") self.selectNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() + 1)) self.selectPrevNodeBtn = QtWidgets.QPushButton("Prev point") self.selectPrevNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() - 1)) self.edit3d_countLabel = QtWidgets.QLabel() self.snapToDtmBtn = QtWidgets.QPushButton("Snap to:") self.snapToDtmBtn.clicked.connect(self.snapToDtm) self.remonveNodeBtn = QtWidgets.QPushButton("Remove") self.remonveNodeBtn.clicked.connect(self.removeNode) nextBox = QtWidgets.QHBoxLayout() nextBox.addWidget(QtWidgets.QLabel("Current point:")) nextBox.addWidget(self.edit3d_currPointId) nextBox.addWidget(QtWidgets.QLabel("/")) nextBox.addWidget(self.edit3d_countLabel) nextBox.addStretch() nextBox.addWidget(self.snapToDtmBtn) nextBox.addWidget(self.edit3d_dtmlayerbox) nextBox.addWidget(self.remonveNodeBtn) nextBox.addWidget(self.selectPrevNodeBtn) nextBox.addWidget(self.selectNodeBtn) ls.addRow(nextBox) self.nodeLayerChanged() if name == "Export": exp2mod, exp2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsTranslate", "opalsTranslate", self.project, { 'oformat': 'shp', 'inFile': 'modelled_lines.shp', 'outFile': 'STRULI3D.shp', }, ["inFile", "outFile"]) self.modules['exp'] = exp2mod ls.addRow(exp2scroll) vl = QtWidgets.QVBoxLayout() vl.addLayout(ls, 1) navbar = QtWidgets.QHBoxLayout() next = QtWidgets.QPushButton("Next step >") next.clicked.connect(self.switchToNextTab) prev = QtWidgets.QPushButton("< Previous step") prev.clicked.connect(self.switchToPrevTab) runcurr = QtWidgets.QPushButton( "Run this step (all modules above)") runcurr.clicked.connect(lambda: self.run_step(None)) if idx > 0: navbar.addWidget(prev) navbar.addStretch() if name in [ "DTM", "Slope", "2D-Approximation", "Topologic correction", "3D-Modelling", "Export" ]: navbar.addWidget(runcurr) navbar.addStretch() if idx < len(self.names): navbar.addWidget(next) vl.addLayout(navbar) self.widgets[name].setLayout(vl) self.tabs.addTab(self.widgets[name], name) # set up connections self.tabs.currentChanged.connect(self.updateTabs) return self.scrollwidget
def getSpinBoxIteration(wgt, value): sp = QSpinBox( wgt) sp.setRange(0, 3) sp.setSingleStep(1) sp.setValue(value) return sp
class LoadOutputAsLayerDialog(QDialog, FORM_CLASS): """ Dialog to load an oq-engine output as layer """ init_done = pyqtSignal(QDialog) loading_completed = pyqtSignal(QDialog) loading_exception = pyqtSignal(QDialog, Exception) def __init__(self, drive_engine_dlg, iface, viewer_dock, session, hostname, calc_id, output_type=None, path=None, mode=None, zonal_layer_path=None, engine_version=None, calculation_mode=None): # sanity check if output_type not in OQ_TO_LAYER_TYPES: raise NotImplementedError(output_type) self.drive_engine_dlg = drive_engine_dlg self.iface = iface self.viewer_dock = viewer_dock self.path = path self.session = session self.hostname = hostname self.calc_id = calc_id self.output_type = output_type self.mode = mode # if 'testing' it will avoid some user interaction self.zonal_layer_path = zonal_layer_path self.engine_version = engine_version self.calculation_mode = calculation_mode QDialog.__init__(self) # Set up the user interface from Designer. self.setupUi(self) # Disable ok_button until all user options are set self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok) self.ok_button.setDisabled(True) self.oqparam = self.drive_engine_dlg.get_oqparam() def on_extract_error(self, exception): if isinstance(exception, TaskCanceled): msg = 'Data extraction canceled' log_msg(msg, level='W', message_bar=self.iface.messageBar()) else: log_msg('Unable to complete data extraction', level='C', message_bar=self.iface.messageBar(), exception=exception) self.reject() def finalize_init(self, extracted_npz): self.npz_file = extracted_npz self.populate_out_dep_widgets() self.adjustSize() self.set_ok_button() self.show() self.init_done.emit(self) def create_num_sites_indicator(self): self.num_sites_msg = 'Number of sites: %s' self.num_sites_lbl = QLabel(self.num_sites_msg % '') self.vlayout.addWidget(self.num_sites_lbl) def create_file_size_indicator(self): self.file_size_msg = 'File size: %s' self.file_size_lbl = QLabel(self.file_size_msg % '') self.vlayout.addWidget(self.file_size_lbl) def create_single_layer_ckb(self): self.load_single_layer_ckb = QCheckBox( 'Load one layer containing all hazard maps') self.vlayout.addWidget(self.load_single_layer_ckb) def create_load_one_layer_per_stat_ckb(self): self.load_one_layer_per_stat_ckb = QCheckBox( 'Load one layer per realization or statistic') self.vlayout.addWidget(self.load_one_layer_per_stat_ckb) def create_min_mag_dsb(self, min_mag=4.0): self.min_mag_lbl = QLabel() self.min_mag_dsb = QDoubleSpinBox(self) self.min_mag_dsb.setRange(0, 10) self.min_mag_dsb.setDecimals(1) self.min_mag_dsb.setSingleStep(0.1) self.min_mag_dsb.setValue(min_mag) self.vlayout.addWidget(self.min_mag_lbl) self.vlayout.addWidget(self.min_mag_dsb) # NOTE: if we don't modify the text of the label after adding the # widget to the layout, the adjustSize does not work properly, for some # unknown reason self.min_mag_lbl.setText('Minimum magnitude') def create_rlz_or_stat_selector(self, all_ckb=False, label='Realization'): self.rlz_or_stat_lbl = QLabel(label) self.rlz_or_stat_cbx = QComboBox() self.rlz_or_stat_cbx.setEnabled(False) self.rlz_or_stat_cbx.currentIndexChanged['QString'].connect( self.on_rlz_or_stat_changed) if all_ckb: self.load_all_rlzs_or_stats_chk = QCheckBox( 'Load all realizations') self.load_all_rlzs_or_stats_chk.stateChanged[int].connect( self.on_load_all_rlzs_or_stats_chk_stateChanged) self.vlayout.addWidget(self.load_all_rlzs_or_stats_chk) self.vlayout.addWidget(self.rlz_or_stat_lbl) self.vlayout.addWidget(self.rlz_or_stat_cbx) def on_load_all_rlzs_or_stats_chk_stateChanged(self, state): self.rlz_or_stat_cbx.setEnabled(state == Qt.Unchecked) def create_selector(self, name, label_text, filter_ckb=False, add_to_layout=None, on_text_changed=None): if add_to_layout is not None: layout = add_to_layout else: layout = self.vlayout setattr(self, "%s_lbl" % name, QLabel(label_text)) setattr(self, "%s_cbx" % name, QComboBox()) lbl = getattr(self, "%s_lbl" % name) cbx = getattr(self, "%s_cbx" % name) cbx.setDisabled(filter_ckb) if on_text_changed is not None: cbx.currentTextChanged['QString'].connect(on_text_changed) if filter_ckb: setattr(self, "filter_by_%s_ckb" % name, QCheckBox('Filter by %s' % name)) filter_ckb = getattr(self, "filter_by_%s_ckb" % name) def on_load_all_ckb_changed(): cbx.setEnabled(filter_ckb.isChecked()) filter_ckb.stateChanged[int].connect(on_load_all_ckb_changed) filter_ckb.setChecked(False) layout.addWidget(filter_ckb) layout.addWidget(lbl) layout.addWidget(cbx) def create_imt_selector(self, all_ckb=False): self.imt_lbl = QLabel('Intensity Measure Type') self.imt_cbx = QComboBox() self.imt_cbx.setEnabled(False) self.imt_cbx.currentIndexChanged['QString'].connect( self.on_imt_changed) if all_ckb: self.load_all_imts_chk = QCheckBox('Load all IMTs') self.load_all_imts_chk.stateChanged[int].connect( self.on_load_all_imts_chk_stateChanged) self.vlayout.addWidget(self.load_all_imts_chk) self.vlayout.addWidget(self.imt_lbl) self.vlayout.addWidget(self.imt_cbx) def on_load_all_imts_chk_stateChanged(self, state): self.imt_cbx.setEnabled(state == Qt.Unchecked) def create_poe_selector(self, all_ckb=False): self.poe_lbl = QLabel('Probability of Exceedance') self.poe_cbx = QComboBox() self.poe_cbx.setEnabled(False) self.poe_cbx.currentIndexChanged['QString'].connect( self.on_poe_changed) if all_ckb: self.load_all_poes_chk = QCheckBox('Load all PoEs') self.load_all_poes_chk.stateChanged[int].connect( self.on_load_all_poes_chk_stateChanged) self.vlayout.addWidget(self.load_all_poes_chk) self.vlayout.addWidget(self.poe_lbl) self.vlayout.addWidget(self.poe_cbx) def on_load_all_poes_chk_stateChanged(self, state): self.poe_cbx.setEnabled(state == Qt.Unchecked) def create_loss_type_selector(self): self.loss_type_lbl = QLabel('Loss Type') self.loss_type_cbx = QComboBox() self.loss_type_cbx.setEnabled(False) self.loss_type_cbx.currentIndexChanged['QString'].connect( self.on_loss_type_changed) self.vlayout.addWidget(self.loss_type_lbl) self.vlayout.addWidget(self.loss_type_cbx) def create_eid_selector(self): self.eid_lbl = QLabel('Event ID') self.eid_sbx = QSpinBox() self.eid_sbx.setEnabled(False) self.vlayout.addWidget(self.eid_lbl) self.vlayout.addWidget(self.eid_sbx) def create_dmg_state_selector(self): self.dmg_state_lbl = QLabel('Damage state') self.dmg_state_cbx = QComboBox() self.dmg_state_cbx.setEnabled(False) self.dmg_state_cbx.currentIndexChanged['QString'].connect( self.on_dmg_state_changed) self.vlayout.addWidget(self.dmg_state_lbl) self.vlayout.addWidget(self.dmg_state_cbx) def create_taxonomy_selector(self): self.taxonomy_lbl = QLabel('Taxonomy') self.taxonomy_cbx = QComboBox() self.taxonomy_cbx.setEnabled(False) self.vlayout.addWidget(self.taxonomy_lbl) self.vlayout.addWidget(self.taxonomy_cbx) def create_style_by_selector(self): self.style_by_lbl = QLabel('Style by') self.style_by_cbx = QComboBox() self.vlayout.addWidget(self.style_by_lbl) self.vlayout.addWidget(self.style_by_cbx) def create_load_selected_only_ckb(self): self.load_selected_only_ckb = QCheckBox("Load only the selected items") self.load_selected_only_ckb.setChecked(True) self.vlayout.addWidget(self.load_selected_only_ckb) def create_show_return_period_ckb(self): self.show_return_period_chk = QCheckBox( "Show the return period in layer names") self.show_return_period_chk.setChecked(False) self.vlayout.addWidget(self.show_return_period_chk) def create_aggregate_by_site_ckb(self): self.aggregate_by_site_ckb = QCheckBox("Aggregate by site") self.aggregate_by_site_ckb.setChecked(True) self.vlayout.addWidget(self.aggregate_by_site_ckb) def create_zonal_layer_selector(self, discard_nonmatching=True): self.added_zonal_layer = None self.zonal_layer_gbx = QGroupBox() self.zonal_layer_gbx.setTitle('Aggregate by zone') self.zonal_layer_gbx.setCheckable(True) self.zonal_layer_gbx.setChecked(False) self.zonal_layer_gbx_v_layout = QVBoxLayout() self.zonal_layer_gbx.setLayout(self.zonal_layer_gbx_v_layout) self.zonal_layer_cbx = QComboBox() self.zonal_layer_cbx.addItem('') self.zonal_layer_lbl = QLabel('Zonal layer') self.zonal_layer_tbn = QToolButton() self.zonal_layer_tbn.setText('...') self.discard_nonmatching_chk = QCheckBox( 'Discard zones with no points') self.discard_nonmatching_chk.setChecked(discard_nonmatching) self.zonal_layer_h_layout = QHBoxLayout() self.zonal_layer_h_layout.addWidget(self.zonal_layer_cbx) self.zonal_layer_h_layout.addWidget(self.zonal_layer_tbn) self.zonal_layer_gbx_v_layout.addWidget(self.zonal_layer_lbl) self.zonal_layer_gbx_v_layout.addLayout(self.zonal_layer_h_layout) self.zonal_layer_gbx_v_layout.addWidget(self.discard_nonmatching_chk) self.vlayout.addWidget(self.zonal_layer_gbx) self.zonal_layer_tbn.clicked.connect(self.open_load_zonal_layer_dialog) self.zonal_layer_cbx.currentIndexChanged[int].connect( self.on_zonal_layer_cbx_currentIndexChanged) self.zonal_layer_gbx.toggled[bool].connect( self.on_zonal_layer_gbx_toggled) self.iface.layerTreeView().currentLayerChanged.connect( self.on_currentLayerChanged) def on_currentLayerChanged(self): self.pre_populate_zonal_layer_cbx() def pre_populate_zonal_layer_cbx(self): # populate cbx only with vector layers containing polygons self.zonal_layer_cbx.clear() for key, layer in QgsProject.instance().mapLayers().items(): if layer.type() != QgsMapLayer.VectorLayer: continue if layer.geometryType() == QgsWkbTypes.PolygonGeometry: self.zonal_layer_cbx.addItem(layer.name()) self.zonal_layer_cbx.setItemData( self.zonal_layer_cbx.count() - 1, layer.id()) if self.added_zonal_layer is not None: self.zonal_layer_cbx.setCurrentIndex( self.zonal_layer_cbx.findData(self.added_zonal_layer.id())) self.zonal_layer_gbx.setChecked( self.zonal_layer_cbx.currentIndex() != -1) def on_zonal_layer_cbx_currentIndexChanged(self, new_index): self.zonal_layer = None if not self.zonal_layer_cbx.currentText(): if self.zonal_layer_gbx.isChecked(): self.ok_button.setEnabled(False) return zonal_layer_id = self.zonal_layer_cbx.itemData(new_index) self.zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id) self.set_ok_button() def on_zonal_layer_gbx_toggled(self, is_checked): if is_checked and not self.zonal_layer_cbx.currentText(): self.ok_button.setEnabled(False) else: self.set_ok_button() def on_output_type_changed(self): if self.output_type in OQ_TO_LAYER_TYPES: self.create_load_selected_only_ckb() self.set_ok_button() def on_rlz_or_stat_changed(self): self.dataset = self.npz_file[self.rlz_or_stat_cbx.currentText()] self.set_ok_button() def on_loss_type_changed(self): self.set_ok_button() def on_imt_changed(self): self.set_ok_button() def on_poe_changed(self): self.set_ok_button() def on_eid_changed(self): self.set_ok_button() def on_dmg_state_changed(self): self.set_ok_button() def populate_out_dep_widgets(self): self.populate_rlz_or_stat_cbx() self.show_num_sites() def get_taxonomies(self): raise NotImplementedError() def populate_rlz_or_stat_cbx(self): self.rlzs_or_stats = [ key for key in sorted(self.npz_file) if key not in ('imtls', 'array') ] self.rlz_or_stat_cbx.clear() self.rlz_or_stat_cbx.setEnabled(True) self.rlz_or_stat_cbx.addItems(self.rlzs_or_stats) def populate_loss_type_cbx(self, loss_types): self.loss_type_cbx.clear() self.loss_type_cbx.setEnabled(True) self.loss_type_cbx.addItems(loss_types) def show_num_sites(self): # NOTE: we are assuming all realizations have the same number of sites, # which currently is always true. # If different realizations have a different number of sites, we # need to move this block of code inside on_rlz_or_stat_changed() rlz_or_stat_data = self.npz_file[self.rlz_or_stat_cbx.currentText()] self.num_sites_lbl.setText(self.num_sites_msg % rlz_or_stat_data.shape) def set_ok_button(self): raise NotImplementedError() def build_layer_name(self, *args, **kwargs): raise NotImplementedError() def get_field_types(self, **kwargs): raise NotImplementedError() def read_npz_into_layer(self, field_types, **kwargs): raise NotImplementedError() def load_from_npz(self): raise NotImplementedError() def add_field_to_layer(self, field_name, field_type): # NOTE: add_attribute use the native qgis editing manager added_field_name = add_attribute(field_name, field_type, self.layer) return added_field_name def get_investigation_time(self): if self.output_type in ('hcurves', 'uhs', 'hmaps', 'ruptures'): try: investigation_time = self.npz_file['investigation_time'] except KeyError as exc: msg = ('investigation_time not found. It is mandatory for %s.' ' Please check if the ouptut was produced by an' ' obsolete version of the OpenQuake Engine' ' Server.') % self.output_type log_msg(msg, level='C', message_bar=self.iface.messageBar(), exception=exc) else: # We must cast to 'str' to keep numerical padding # after saving the project return str(investigation_time) else: # some outputs do not need the investigation time return None def build_layer(self, rlz_or_stat=None, taxonomy=None, poe=None, loss_type=None, dmg_state=None, gsim=None, imt=None, boundaries=None, geometry_type='point', wkt_geom_type=None, row_wkt_geom_types=None, add_to_group=None, add_to_map=True): layer_name = self.build_layer_name(rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, gsim=gsim, imt=imt, geometry_type=geometry_type) field_types = self.get_field_types(rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, imt=imt) # create layer self.layer = QgsVectorLayer("%s?crs=epsg:4326" % geometry_type, layer_name, "memory") modified_field_types = copy.copy(field_types) for field_name, field_type in field_types.items(): if field_name in ['lon', 'lat', 'boundary']: continue added_field_name = self.add_field_to_layer(field_name, field_type) if field_name != added_field_name: if field_name == self.default_field_name: self.default_field_name = added_field_name # replace field_name with the actual added_field_name del modified_field_types[field_name] modified_field_types[added_field_name] = field_type field_types = copy.copy(modified_field_types) self.layer = self.read_npz_into_layer( field_types, rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, imt=imt, boundaries=boundaries, geometry_type=geometry_type, wkt_geom_type=wkt_geom_type, row_wkt_geom_types=row_wkt_geom_types) if (self.output_type == 'damages-rlzs' and not self.aggregate_by_site_ckb.isChecked()): self.layer.setCustomProperty('output_type', 'recovery_curves') else: self.layer.setCustomProperty('output_type', self.output_type) investigation_time = self.get_investigation_time() if investigation_time is not None: self.layer.setCustomProperty('investigation_time', investigation_time) if self.engine_version is not None: self.layer.setCustomProperty('engine_version', self.engine_version) irmt_version = get_irmt_version() self.layer.setCustomProperty('irmt_version', irmt_version) self.layer.setCustomProperty('calc_id', self.calc_id) if poe is not None: self.layer.setCustomProperty('poe', poe) user_params = { 'rlz_or_stat': rlz_or_stat, 'taxonomy': taxonomy, 'poe': poe, 'loss_type': loss_type, 'dmg_state': dmg_state, 'gsim': gsim, 'imt': imt } write_metadata_to_layer(self.drive_engine_dlg, self.output_type, self.layer, user_params) try: if (self.zonal_layer_cbx.currentText() and self.zonal_layer_gbx.isChecked()): return except AttributeError: # the aggregation stuff might not exist for some loaders pass if add_to_map: if add_to_group: tree_node = add_to_group else: tree_node = QgsProject.instance().layerTreeRoot() if self.mode != 'testing': # NOTE: the following commented line would cause (unexpectedly) # "QGIS died on signal 11" and double creation of some # layers during integration tests QgsProject.instance().addMapLayer(self.layer, False) tree_node.insertLayer(0, self.layer) self.iface.setActiveLayer(self.layer) if add_to_group: # NOTE: zooming to group from caller function, to avoid # repeating it once per layer pass else: self.iface.zoomToActiveLayer() log_msg('Layer %s was created successfully' % layer_name, level='S', message_bar=self.iface.messageBar()) return self.layer @staticmethod def style_maps(layer, style_by, iface, output_type='damages-rlzs', perils=None, add_null_class=False, render_higher_on_top=False, repaint=True, use_sgc_style=False): symbol = QgsSymbol.defaultSymbol(layer.geometryType()) # see properties at: # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073 symbol.setOpacity(1) if isinstance(symbol, QgsMarkerSymbol): # do it only for the layer with points symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen)) style = get_style(layer, iface.messageBar()) # this is the default, as specified in the user settings ramp = QgsGradientColorRamp(style['color_from'], style['color_to']) style_mode = style['style_mode'] # in most cases, we override the user-specified setting, and use # instead a setting that was required by scientists if output_type in OQ_TO_LAYER_TYPES: default_qgs_style = QgsStyle().defaultStyle() default_color_ramp_names = default_qgs_style.colorRampNames() if output_type in ( 'damages-rlzs', 'avg_losses-rlzs', 'avg_losses-stats', ): # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Jenks else: style_mode = 'Jenks' ramp_type_idx = default_color_ramp_names.index('Reds') symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top'])) inverted = False elif (output_type in ('gmf_data', 'ruptures') or (output_type == 'hmaps' and not use_sgc_style)): # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if output_type == 'ruptures': if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Pretty else: style_mode = 'PrettyBreaks' else: if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.EqualInterval else: style_mode = 'EqualInterval' ramp_type_idx = default_color_ramp_names.index('Spectral') inverted = True symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top'])) elif output_type == 'hmaps' and use_sgc_style: # FIXME: for SGC they were using size 10000 map units # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Pretty else: style_mode = 'PrettyBreaks' try: ramp_type_idx = default_color_ramp_names.index( 'SGC_Green2Red_Hmap_Color_Ramp') except ValueError: raise ValueError( 'Color ramp SGC_Green2Red_Hmap_Color_Ramp was ' 'not found. Please import it from ' 'Settings -> Style Manager, loading ' 'svir/resources/sgc_green2red_hmap_color_ramp.xml') inverted = False registry = QgsApplication.symbolLayerRegistry() symbol_props = { 'name': 'square', 'color': '0,0,0', 'color_border': '0,0,0', 'offset': '0,0', 'size': '1.5', # FIXME 'angle': '0', } square = registry.symbolLayerMetadata( "SimpleMarker").createSymbolLayer(symbol_props) symbol = QgsSymbol.defaultSymbol(layer.geometryType()).clone() symbol.deleteSymbolLayer(0) symbol.appendSymbolLayer(square) symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen)) elif output_type in ['asset_risk', 'input']: # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.EqualInterval else: style_mode = 'EqualInterval' # exposure_strings = ['number', 'occupants', 'value'] # setting exposure colors by default colors = { 'single': RAMP_EXTREME_COLORS['Blues']['top'], 'ramp_name': 'Blues' } inverted = False if output_type == 'asset_risk': damage_strings = perils for damage_string in damage_strings: if damage_string in style_by: colors = { 'single': RAMP_EXTREME_COLORS['Spectral']['top'], 'ramp_name': 'Spectral' } inverted = True break else: # 'input' colors = { 'single': RAMP_EXTREME_COLORS['Greens']['top'], 'ramp_name': 'Greens' } symbol.symbolLayer(0).setShape( QgsSimpleMarkerSymbolLayerBase.Square) single_color = colors['single'] ramp_name = colors['ramp_name'] ramp_type_idx = default_color_ramp_names.index(ramp_name) symbol.setColor(QColor(single_color)) else: raise NotImplementedError( 'Undefined color ramp for output type %s' % output_type) ramp = default_qgs_style.colorRamp( default_color_ramp_names[ramp_type_idx]) if inverted: ramp.invert() # get unique values fni = layer.fields().indexOf(style_by) unique_values = layer.dataProvider().uniqueValues(fni) num_unique_values = len(unique_values - {NULL}) if num_unique_values > 2: if Qgis.QGIS_VERSION_INT < 31000: renderer = QgsGraduatedSymbolRenderer.createRenderer( layer, style_by, min(num_unique_values, style['classes']), style_mode, symbol.clone(), ramp) else: renderer = QgsGraduatedSymbolRenderer(style_by, []) # NOTE: the following returns an instance of one of the # subclasses of QgsClassificationMethod classification_method = \ QgsApplication.classificationMethodRegistry().method( style_mode) renderer.setClassificationMethod(classification_method) renderer.updateColorRamp(ramp) renderer.updateSymbols(symbol.clone()) renderer.updateClasses( layer, min(num_unique_values, style['classes'])) if not use_sgc_style: if Qgis.QGIS_VERSION_INT < 31000: label_format = renderer.labelFormat() # NOTE: the following line might be useful # label_format.setTrimTrailingZeroes(True) label_format.setPrecision(2) renderer.setLabelFormat(label_format, updateRanges=True) else: renderer.classificationMethod().setLabelPrecision(2) renderer.calculateLabelPrecision() elif num_unique_values == 2: categories = [] for unique_value in unique_values: symbol = symbol.clone() try: symbol.setColor( QColor(RAMP_EXTREME_COLORS[ramp_name] ['bottom' if unique_value == min(unique_values) else 'top'])) except Exception: symbol.setColor( QColor(style['color_from'] if unique_value == min(unique_values) else style['color_to'])) category = QgsRendererCategory(unique_value, symbol, str(unique_value)) # entry for the list of category items categories.append(category) renderer = QgsCategorizedSymbolRenderer(style_by, categories) else: renderer = QgsSingleSymbolRenderer(symbol.clone()) if add_null_class and NULL in unique_values: # add a class for NULL values rule_renderer = QgsRuleBasedRenderer(symbol.clone()) root_rule = rule_renderer.rootRule() not_null_rule = root_rule.children()[0].clone() # strip parentheses from stringified color HSL not_null_rule.setFilterExpression( '%s IS NOT NULL' % QgsExpression.quotedColumnRef(style_by)) not_null_rule.setLabel('%s:' % style_by) root_rule.appendChild(not_null_rule) null_rule = root_rule.children()[0].clone() null_rule.setSymbol( QgsFillSymbol.createSimple({ 'color': '160,160,160', 'style': 'diagonal_x' })) null_rule.setFilterExpression( '%s IS NULL' % QgsExpression.quotedColumnRef(style_by)) null_rule.setLabel(tr('No points')) root_rule.appendChild(null_rule) if isinstance(renderer, QgsGraduatedSymbolRenderer): # create value ranges rule_renderer.refineRuleRanges(not_null_rule, renderer) # remove default rule elif isinstance(renderer, QgsCategorizedSymbolRenderer): rule_renderer.refineRuleCategoris(not_null_rule, renderer) for rule in rule_renderer.rootRule().children()[1].children(): label = rule.label() # by default, labels are like: # ('"collapse-structural-ASH_DRY_sum" >= 0.0000 AND # "collapse-structural-ASH_DRY_sum" <= 2.3949') first, second = label.split(" AND ") bottom = first.rsplit(" ", 1)[1] top = second.rsplit(" ", 1)[1] simplified = "%s - %s" % (bottom, top) rule.setLabel(simplified) root_rule.removeChildAt(0) renderer = rule_renderer if render_higher_on_top: renderer.setUsingSymbolLevels(True) symbol_items = [item for item in renderer.legendSymbolItems()] for i in range(len(symbol_items)): sym = symbol_items[i].symbol().clone() key = symbol_items[i].ruleKey() for lay in range(sym.symbolLayerCount()): sym.symbolLayer(lay).setRenderingPass(i) renderer.setLegendSymbolItem(key, sym) layer.setRenderer(renderer) if not use_sgc_style: layer.setOpacity(0.7) if repaint: layer.triggerRepaint() iface.setActiveLayer(layer) iface.zoomToActiveLayer() # NOTE QGIS3: probably not needed # iface.layerTreeView().refreshLayerSymbology(layer.id()) iface.mapCanvas().refresh() def style_categorized(self, layer=None, style_by=None): if layer is None: layer = self.layer if style_by is None: style_by = self.default_field_name # get unique values fni = layer.fields().indexOf(style_by) unique_values = layer.dataProvider().uniqueValues(fni) # define categories categories = [] for unique_value in unique_values: # initialize the default symbol for this geometry type symbol = QgsSymbol.defaultSymbol(layer.geometryType()) # configure a symbol layer layer_style = {} layer_style['color'] = '%d, %d, %d' % (randrange( 0, 256), randrange(0, 256), randrange(0, 256)) layer_style['outline'] = '#000000' symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style) # replace default symbol layer with the configured one if symbol_layer is not None: symbol.changeSymbolLayer(0, symbol_layer) # create renderer object category = QgsRendererCategory(unique_value, symbol, str(unique_value)) # entry for the list of category items categories.append(category) # create renderer object renderer = QgsCategorizedSymbolRenderer(style_by, categories) # assign the created renderer to the layer if renderer is not None: layer.setRenderer(renderer) layer.triggerRepaint() # NOTE QGIS3: probably not needed # self.iface.layerTreeView().refreshLayerSymbology(layer.id()) self.iface.mapCanvas().refresh() def style_curves(self): registry = QgsApplication.symbolLayerRegistry() symbol_props = { 'name': 'cross2', 'color': '0,0,0', 'color_border': '0,0,0', 'offset': '0,0', 'size': '1.5', 'angle': '0', } opacity = 0.7 cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer( symbol_props) # NOTE: Cross symbols rendered for OQ-Engine disaggregation outputs are # opaque, wider and thicker than those used for other outputs (e.g. # hcurves) if self.output_type == 'disagg': cross.setSize(3) cross.setStrokeWidth(0.5) opacity = 1 symbol = QgsSymbol.defaultSymbol(self.layer.geometryType()).clone() symbol.deleteSymbolLayer(0) symbol.appendSymbolLayer(cross) renderer = QgsSingleSymbolRenderer(symbol) effect = QgsOuterGlowEffect() effect.setSpread(0.5) effect.setOpacity(opacity) effect.setColor(QColor(255, 255, 255)) effect.setBlurLevel(1) renderer.paintEffect().appendEffect(effect) renderer.paintEffect().setEnabled(True) self.layer.setRenderer(renderer) self.layer.setOpacity(opacity) self.layer.triggerRepaint() # NOTE QGIS3: probably not needed # self.iface.layerTreeView().refreshLayerSymbology(self.layer.id()) self.iface.mapCanvas().refresh() def open_load_zonal_layer_dialog(self): """ Open a file dialog to select the zonal layer to be loaded :returns: the zonal layer """ text = self.tr('Select zonal layer to import') filters = self.tr('All files (*.*);;' 'GeoPackages (*.gpkg);;' 'Vector shapefiles (*.shp);;' 'SQLite (*.sqlite);;') default_dir = QSettings().value('irmt/select_layer_dir', QDir.homePath()) file_name, _ = QFileDialog.getOpenFileName(self, text, default_dir, filters) if not file_name: return None selected_dir = QFileInfo(file_name).dir().path() QSettings().setValue('irmt/select_layer_dir', selected_dir) zonal_layer = self.load_zonal_layer(file_name) return zonal_layer def load_zonal_layer(self, zonal_layer_path): self.added_zonal_layer = zonal_layer = None zonal_layer_basename, zonal_layer_ext = os.path.splitext( os.path.basename(zonal_layer_path)) if zonal_layer_ext == '.gpkg': dlg = QgsSublayersDialog(QgsSublayersDialog.Ogr, 'Select zonal layer') conn = ogr.Open(zonal_layer_path) layer_defs = [] for idx, c in enumerate(conn): ld = QgsSublayersDialog.LayerDefinition() ld.layerId = idx ld.layerName = c.GetDescription() ld.count = c.GetFeatureCount() ld.type = ogr.GeometryTypeToName(c.GetGeomType()) layer_defs.append(ld) dlg.populateLayerTable(layer_defs) dlg.exec_() if not dlg.selection(): return None for sel in dlg.selection(): # NOTE: the last one will be chosen as zonal layer zonal_layer = QgsVectorLayer( zonal_layer_path + "|layername=" + sel.layerName, sel.layerName, 'ogr') if zonal_layer.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer, False) root.insertLayer(0, zonal_layer) else: msg = 'Invalid layer' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None else: zonal_layer = QgsVectorLayer(zonal_layer_path, zonal_layer_basename, 'ogr') if not zonal_layer.geometryType() == QgsWkbTypes.PolygonGeometry: msg = 'Zonal layer must contain zone polygons' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None if zonal_layer_ext != '.gpkg': # Add zonal layer to registry if zonal_layer.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer, False) root.insertLayer(0, zonal_layer) else: msg = 'Invalid zonal layer' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None self.added_zonal_layer = zonal_layer self.pre_populate_zonal_layer_cbx() return zonal_layer def populate_zonal_layer_cbx(self, zonal_layer): cbx = self.zonal_layer_cbx cbx.addItem(zonal_layer.name()) last_index = cbx.count() - 1 cbx.setItemData(last_index, zonal_layer.id()) cbx.setCurrentIndex(last_index) def show_file_size(self): file_size = get_file_size(self.path) self.file_size_lbl.setText(self.file_size_msg % file_size) def accept(self): log_msg('Loading output started. Watch progress in QGIS task bar', level='I', message_bar=self.iface.messageBar()) try: self.iface.layerTreeView().currentLayerChanged.disconnect( self.on_currentLayerChanged) except Exception: # it's connected only for some loaders pass self.hide() if self.output_type in OQ_EXTRACT_TO_LAYER_TYPES: self.load_from_npz() if self.output_type in ('avg_losses-rlzs', 'damages-rlzs', 'avg_losses-stats'): # check if also aggregating by zone or not if (not self.zonal_layer_cbx.currentText() or not self.zonal_layer_gbx.isChecked()): super().accept() return self.aggregate_by_zone() else: super().accept() elif self.output_type in OQ_CSV_TO_LAYER_TYPES: self.load_from_csv() super().accept() def aggregate_by_zone(self): loss_layer = self.layer zonal_layer_id = self.zonal_layer_cbx.itemData( self.zonal_layer_cbx.currentIndex()) zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id) QgsProject.instance().layerTreeRoot().findLayer( zonal_layer.id()).setItemVisibilityChecked(False) # if the two layers have different projections, display a # warning, but try proceeding anyway have_same_projection, check_projection_msg = ProcessLayer( loss_layer).has_same_projection_as(zonal_layer) if not have_same_projection: log_msg(check_projection_msg, level='W', message_bar=self.iface.messageBar()) try: [self.loss_attr_name ] = [field.name() for field in loss_layer.fields()] except ValueError: self.loss_attr_name = self.default_field_name zonal_layer_plus_sum_name = "%s: %s_sum" % (zonal_layer.name(), self.loss_attr_name) discard_nonmatching = self.discard_nonmatching_chk.isChecked() try: calculate_zonal_stats(self.on_calculate_zonal_stats_completed, zonal_layer, loss_layer, [self.loss_attr_name], zonal_layer_plus_sum_name, discard_nonmatching=discard_nonmatching, predicates=('intersects', ), summaries=('sum', )) except Exception as exc: log_msg(str(exc), level='C', message_bar=self.iface.messageBar(), exception=exc) def on_calculate_zonal_stats_completed(self, zonal_layer_plus_sum): if zonal_layer_plus_sum is None: msg = 'The calculation of zonal statistics was not completed' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None # Add zonal layer to registry if zonal_layer_plus_sum.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer_plus_sum, False) root.insertLayer(0, zonal_layer_plus_sum) else: msg = 'The layer aggregating data by zone is invalid.' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None # NOTE: in scenario damage, keys are like # u'structural_no_damage_mean', and not just # u'structural', therefore we can't just use the selected # loss type, but we must use the actual only key in the # dict added_loss_attr = "%s_sum" % self.loss_attr_name style_by = added_loss_attr try: perils = self.perils except AttributeError: perils = None self.style_maps(zonal_layer_plus_sum, style_by, self.iface, self.output_type, perils=perils, add_null_class=True) super().accept() def reject(self): try: self.iface.layerTreeView().currentLayerChanged.disconnect( self.on_currentLayerChanged) except Exception: # it's connected only for some loaders pass super().reject()
elif no == 2: self._angle2 = -9999.9 else: self._angle = -9999.9 self._angle2 = -9999.9 self.update() if __name__ == "__main__": from qgis.PyQt.Qt import QApplication app = QApplication(sys.argv) window = QWidget() compass = CompassWidget() spinBox = QSpinBox() spinBox.setRange(0, 719) spinBox.valueChanged.connect(compass.setAngle) spinBox2 = QSpinBox() spinBox2.setRange(0, 719) spinBox2.valueChanged.connect(compass.setAngle2) layout = QVBoxLayout() layout.addWidget(compass) layout.addWidget(spinBox) layout.addWidget(spinBox2) window.setLayout(layout) window.show() sys.exit(app.exec_())
class DistrictSettingsDialog(QDialog): """ A dialog used for plugin settings """ def __init__(self, parent=None): # pylint: disable=too-many-statements super().__init__(parent) self.setWindowTitle(self.tr('Redistrict Plugin | Settings')) layout = QVBoxLayout() self.auth_label = QLabel(self.tr('Authentication configuration')) layout.addWidget(self.auth_label) self.auth_value = QgsAuthConfigSelect() layout.addWidget(self.auth_value) auth_id = get_auth_config_id() if auth_id: self.auth_value.setConfigId(auth_id) layout.addWidget(QLabel(self.tr('API base URL'))) self.base_url_edit = QLineEdit() self.base_url_edit.setText(QgsSettings().value('redistrict/base_url', '', str, QgsSettings.Plugins)) layout.addWidget(self.base_url_edit) h_layout = QHBoxLayout() h_layout.addWidget(QLabel(self.tr('Check for completed requests every'))) self.check_every_spin = QSpinBox() self.check_every_spin.setMinimum(10) self.check_every_spin.setMaximum(600) self.check_every_spin.setSuffix(' ' + self.tr('s')) self.check_every_spin.setValue(QgsSettings().value('redistrict/check_every', '30', int, QgsSettings.Plugins)) h_layout.addWidget(self.check_every_spin) layout.addLayout(h_layout) self.use_mock_checkbox = QCheckBox(self.tr('Use mock Statistics NZ API')) self.use_mock_checkbox.setChecked(get_use_mock_api()) layout.addWidget(self.use_mock_checkbox) self.test_button = QPushButton(self.tr('Test API connection')) self.test_button.clicked.connect(self.test_api) layout.addWidget(self.test_button) self.use_overlays_checkbox = QCheckBox(self.tr('Show updated populations during interactive redistricting')) self.use_overlays_checkbox.setChecked( QgsSettings().value('redistrict/show_overlays', False, bool, QgsSettings.Plugins)) layout.addWidget(self.use_overlays_checkbox) self.use_sound_group_box = QGroupBox(self.tr('Use audio feedback')) self.use_sound_group_box.setCheckable(True) self.use_sound_group_box.setChecked( QgsSettings().value('redistrict/use_audio_feedback', False, bool, QgsSettings.Plugins)) sound_layout = QGridLayout() sound_layout.addWidget(QLabel(self.tr('When meshblock redistricted')), 0, 0) self.on_redistrict_file_widget = QgsFileWidget() self.on_redistrict_file_widget.setDialogTitle(self.tr('Select Audio File')) self.on_redistrict_file_widget.setStorageMode(QgsFileWidget.GetFile) self.on_redistrict_file_widget.setFilePath( QgsSettings().value('redistrict/on_redistrict', '', str, QgsSettings.Plugins)) self.on_redistrict_file_widget.setFilter(self.tr('Wave files (*.wav *.WAV)')) sound_layout.addWidget(self.on_redistrict_file_widget, 0, 1) self.play_on_redistrict_sound_button = QPushButton(self.tr('Test')) self.play_on_redistrict_sound_button.clicked.connect(self.play_on_redistrict_sound) sound_layout.addWidget(self.play_on_redistrict_sound_button, 0, 2) self.use_sound_group_box.setLayout(sound_layout) layout.addWidget(self.use_sound_group_box) button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) layout.addWidget(button_box) button_box.rejected.connect(self.reject) button_box.accepted.connect(self.accept) self.setLayout(layout) def accept(self): # pylint: disable=missing-docstring super().accept() QgsSettings().setValue('redistrict/auth_config_id', self.auth_value.configId(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/use_mock_api', self.use_mock_checkbox.isChecked(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/base_url', self.base_url_edit.text(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/check_every', self.check_every_spin.value(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/show_overlays', self.use_overlays_checkbox.isChecked(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/use_audio_feedback', self.use_sound_group_box.isChecked(), QgsSettings.Plugins) QgsSettings().setValue('redistrict/on_redistrict', self.on_redistrict_file_widget.filePath(), QgsSettings.Plugins) def test_api(self): """ Tests the API connection (real or mock!) """ connector = get_api_connector(use_mock=self.use_mock_checkbox.isChecked(), authcfg=self.auth_value.configId(), base_url=self.base_url_edit.text()) if connector.check(): QMessageBox.information(self, self.tr('Test API Connection'), self.tr('API responded OK!'), QMessageBox.Ok) else: QMessageBox.critical(self, self.tr('Test API Connection'), self.tr('Could not connect to API!'), QMessageBox.Ok) def play_on_redistrict_sound(self): """ Plays the 'on redistrict' sound """ try: playsound(self.on_redistrict_file_widget.filePath(), block=False) except FileNotFoundError: pass
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 __init__(self, iface): QtWidgets.QDialog.__init__(self) self.iface = iface self.setupUi(self) self.error = None self.error = None self.data_path, self.data_type = GetOutputFileName( self, 'AequilibraE custom formats', ["Aequilibrae dataset(*.aed)", "Aequilibrae matrix(*.aem)"], '.aed', standard_path()) if self.data_type is None: self.error = 'Path provided is not a valid dataset' self.exit_with_error() self.data_type = self.data_type.upper() if self.data_type == 'AED': self.data_to_show = AequilibraEData() elif self.data_type == 'AEM': self.data_to_show = AequilibraeMatrix() try: self.data_to_show.load(self.data_path) except: self.error = 'Could not load dataset' self.exit_with_error() # Elements that will be used during the displaying self._layout = QVBoxLayout() self.table = QTableView() self._layout.addWidget(self.table) # Settings for displaying self.show_layout = QHBoxLayout() # Thousand separator self.thousand_separator = QCheckBox() self.thousand_separator.setChecked(True) self.thousand_separator.setText('Thousands separator') self.thousand_separator.toggled.connect(self.format_showing) self.show_layout.addWidget(self.thousand_separator) self.spacer = QSpacerItem(5, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.show_layout.addItem(self.spacer) # Decimals txt = QLabel() txt.setText('Decimal places') self.show_layout.addWidget(txt) self.decimals = QSpinBox() self.decimals.valueChanged.connect(self.format_showing) self.decimals.setMinimum(0) self.decimals.setValue(4) self.decimals.setMaximum(10) self.show_layout.addWidget(self.decimals) self._layout.addItem(self.show_layout) # differentiates between matrix and dataset if self.data_type == 'AEM': self.data_to_show.computational_view([self.data_to_show.names[0]]) # Matrices need cores and indices to be set as well self.mat_layout = QHBoxLayout() self.mat_list = QComboBox() for n in self.data_to_show.names: self.mat_list.addItem(n) self.mat_list.currentIndexChanged.connect(self.change_matrix_cores) self.mat_layout.addWidget(self.mat_list) self.idx_list = QComboBox() for i in self.data_to_show.index_names: self.idx_list.addItem(i) self.idx_list.currentIndexChanged.connect(self.change_matrix_cores) self.mat_layout.addWidget(self.idx_list) self._layout.addItem(self.mat_layout) self.change_matrix_cores() self.but_export = QPushButton() self.but_export.setText('Export') self.but_export.clicked.connect(self.export) self.but_close = QPushButton() self.but_close.clicked.connect(self.exit_procedure) self.but_close.setText('Close') self.but_layout = QHBoxLayout() self.but_layout.addWidget(self.but_export) self.but_layout.addWidget(self.but_close) self._layout.addItem(self.but_layout) # We chose to use QTableView. However, if we want to allow the user to edit the dataset # The we need to allow them to switch to the slower QTableWidget # Code below # self.table = QTableWidget(self.data_to_show.entries, self.data_to_show.num_fields) # self.table.setHorizontalHeaderLabels(self.data_to_show.fields) # self.table.setObjectName('data_viewer') # # self.table.setVerticalHeaderLabels([str(x) for x in self.data_to_show.index[:]]) # self.table.clearContents() # # for i in range(self.data_to_show.entries): # for j, f in enumerate(self.data_to_show.fields): # item1 = QTableWidgetItem(str(self.data_to_show.data[f][i])) # item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) # self.table.setItem(i, j, item1) self.resize(700, 500) self.setLayout(self._layout) self.format_showing()
class QpalsLM(object): def __init__(self, project, layerlist, iface): self.tabs = None self.project = project self.layerlist = layerlist self.iface = iface self.pcfile = None self.multirunner = None def switchToNextTab(self): curridx = self.tabs.currentIndex() self.tabs.setCurrentIndex(curridx + 1) self.updateTabs() def updateTabs(self): curridx = self.tabs.currentIndex() # update tabs infile = self.settings['settings']['inFile'].currentText() if self.names[curridx] == "DTM": self.pcfile = "pointcloud.odm" if infile.endswith(".tif"): self.widgets['dtmGrid'].setEnabled(False) self.widgets['dtmImp'].setEnabled(True) self.modules['dtmImp'].setParam('inFile', infile) self.modules['dtmImp'].setParam('tileSize', '120') self.modules['dtmShade'].setParam('inFile', infile) self.modules['slope'].setParam('inFile', infile) self.modules['dtmGrid'].setParam('outFile', infile) self.addDtm() elif infile.endswith(".odm"): self.widgets['dtmGrid'].setEnabled(True) self.widgets['dtmImp'].setEnabled(False) self.modules['dtmGrid'].setParam('inFile', infile) self.pcfile = infile else: self.widgets['dtmImp'].setEnabled(True) self.widgets['dtmGrid'].setEnabled(True) self.modules['dtmImp'].setParam('inFile', infile) self.modules['dtmGrid'].setParam('inFile', "pointcloud.odm") tempf = self.settings['settings']['tempFolder'].currentText() if not os.path.isdir(tempf): try: os.makedirs(tempf) except: tempf = self.project.tempdir self.settings['settings']['tempFolder'].setText(tempf) self.project.tempdir = tempf self.project.workdir = tempf if self.names[curridx] == "3D-Modelling": self.modules['lm'].setParam('inFile', self.pcfile) if self.names[curridx] != "Editing" or "Editing (3D)": if self.section.ltool.rb: self.section.ltool.canvas.scene().removeItem( self.section.ltool.rb) self.iface.actionPan().trigger() if self.names[curridx] == "Export": outdir = self.settings['settings']['outFolder'].currentText() self.modules['exp'].setParam('outFile', os.path.join(outdir, '3DSTRULI.shp')) if not os.path.isdir(outdir) and outdir: os.makedirs(outdir) def switchToPrevTab(self): curridx = self.tabs.currentIndex() self.tabs.setCurrentIndex(curridx - 1) def snapToDtm(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() rlayer = self.edit3d_dtmlayerbox.currentLayer() llayer.startEditing() points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry() if pointGeom.asMultiPoint(): pointGeom = pointGeom.asMultiPoint()[0] else: pointGeom = pointGeom.asPoint() pid, feat = closestpoint(llayer, QgsGeometry.fromPoint(pointGeom)) linegeom = feat.geometry().asWkb().data() olinegeom = ogr.CreateGeometryFromWkb(linegeom) dx = rlayer.rasterUnitsPerPixelX() dy = rlayer.rasterUnitsPerPixelY() xpos = pointGeom.x() ypos = pointGeom.y() # assume pixel = center xll = rlayer.extent().xMinimum() + 0.5 * dx yll = rlayer.extent().yMinimum() + 0.5 * dy xoffs = (pointGeom.x() - xll) % dx yoffs = (pointGeom.y() - yll) % dy dtm_val_ll = rlayer.dataProvider().identify( QgsPoint(xpos - dx / 2, ypos - dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_ur = rlayer.dataProvider().identify( QgsPoint(xpos + dx / 2, ypos + dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_lr = rlayer.dataProvider().identify( QgsPoint(xpos + dx / 2, ypos - dy / 2), QgsRaster.IdentifyFormatValue).results()[1] dtm_val_ul = rlayer.dataProvider().identify( QgsPoint(xpos - dx / 2, ypos + dy / 2), QgsRaster.IdentifyFormatValue).results()[1] a00 = dtm_val_ll a10 = dtm_val_lr - dtm_val_ll a01 = dtm_val_ul - dtm_val_ll a11 = dtm_val_ur + dtm_val_ll - (dtm_val_lr + dtm_val_ul) dtm_bilinear = a00 + a10 * xoffs + a01 * yoffs + a11 * xoffs * yoffs x, y = olinegeom.GetPoint_2D(pid) olinegeom.SetPoint(pid, x, y, dtm_bilinear) llayer.beginEditCommand("Snap point height to DTM") updatedGeom = QgsGeometry() updatedGeom.fromWkb(olinegeom.ExportToWkb()) llayer.dataProvider().changeGeometryValues( {feat.id(): updatedGeom}) llayer.endEditCommand() # refresh vertex editor self.showProblemPoint() def removeNode(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() llayer.startEditing() points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry() pid, feat = closestpoint(llayer, pointGeom) llayer.beginEditCommand("Vertex removed") llayer.deleteVertex(feat.id(), pid) llayer.endEditCommand() def nextProblemPoint(self): pointid = self.edit3d_currPointId.value() self.edit3d_currPointId.setValue(pointid + 1) def showProblemPoint(self): player = self.edit3d_pointlayerbox.currentLayer() llayer = self.edit3d_linelayerbox.currentLayer() self.iface.setActiveLayer(llayer) mc = self.iface.mapCanvas() # get first layer #llayer.startEditing() #self.iface.actionVertexTool().trigger() # get point position points = list(player.getFeatures()) pointid = self.edit3d_currPointId.value() if points: point = points[pointid] pointGeom = point.geometry().asMultiPoint()[0] if point.geometry( ).asMultiPoint() else point.geometry().asPoint() t = QgsCoordinateTransform(llayer.crs(), mc.mapSettings().destinationCrs(), QgsCoordinateTransformContext()) tCenter = t.transform(pointGeom) rect = QgsRectangle(tCenter, tCenter) mc.setExtent(rect) #pos = QgsMapTool(self.iface.mapCanvas()).toCanvasCoordinates(tCenter) #click = QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) #mc.mousePressEvent(click) #mc.mousePressEvent(click) mc.refresh() #vertexDock = \ # [ch for ch in self.iface.mainWindow().findChildren(QDockWidget, "") if # ch.windowTitle() == u'Vertex Editor'] #if vertexDock: # vertexDock = vertexDock[0] # self.editingls.addWidget(vertexDock) mc.refresh() def nodeLayerChanged(self): if self.edit3d_pointlayerbox.currentLayer(): self.selectNodeBtn.setText("Next point") self.selectNodeBtn.setEnabled(True) cnt = self.edit3d_pointlayerbox.currentLayer().featureCount() - 1 self.edit3d_countLabel.setText(str(cnt)) self.edit3d_currPointId.setMaximum(cnt) def createWidget(self): self.scrollwidget = QtWidgets.QScrollArea() self.scrollwidget.setWidgetResizable(True) self.tabs = QtWidgets.QTabWidget() self.scrollwidget.setWidget(self.tabs) self.names = [ 'Settings', 'DTM', 'Slope', '2D-Approximation', 'Topologic correction', 'Editing', '3D-Modelling', 'Editing (3D)', 'Export' ] self.widgets = {} self.settings = {} self.modules = {} for idx, name in enumerate(self.names): self.widgets[name] = QtWidgets.QDialog() ls = QtWidgets.QFormLayout() # Tab-specific options if name == "Settings": desc = QtWidgets.QLabel( "Welcome to the qpals LineModeler GUI! \nThis tool will help you to detect and " "model breaklines based on a DTM and/or a point cloud using the opals module " "opalsLineModeler.\nThe process includes manual editing in QGIS (\"Editing\") " "as well as automatic dectection and modelling.\n\n" "To begin, please enter some basic information.") desc.setWordWrap(True) ls.addRow(desc) boxRun = QtWidgets.QGroupBox( "Run multiple steps automatically:") boxVL = QtWidgets.QVBoxLayout() boxRun.setLayout(boxVL) self.settings['settings'] = OrderedDict([ ('name', QtWidgets.QLineEdit()), ('inFile', QpalsDropTextbox.QpalsDropTextbox( layerlist=self.layerlist)), ('tempFolder', QpalsDropTextbox.QpalsDropTextbox()), ('outFolder', QpalsDropTextbox.QpalsDropTextbox()), ('chkDTM', QtWidgets.QCheckBox("DTM")), ('chkSlope', QtWidgets.QCheckBox("Slope")), ('chk2D', QtWidgets.QCheckBox("2D-Approximation")), ('chktopo2D', QtWidgets.QCheckBox("Topological correction")), ('chkEditing2d', QtWidgets.QLabel( "--- Manual editing of 2D-Approximations ---")), ('chk3Dmodel', QtWidgets.QCheckBox("3D-Modelling")), ('chkEditing3d', QtWidgets.QLabel("--- Manual editing of 3D-Lines ---")), ('chkExport', QtWidgets.QCheckBox("Export")), ]) for key, value in list(self.settings['settings'].items()): if isinstance(value, QpalsDropTextbox.QpalsDropTextbox): value.setMinimumContentsLength(20) value.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToMinimumContentsLength) if key.startswith("chk"): boxVL.addWidget(value) ls.addRow(QtWidgets.QLabel("Project name"), self.settings['settings']['name']) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['inFile'], stretch=1) ls.addRow(QtWidgets.QLabel("Input file (TIFF/LAS/ODM)"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() hbox_wrap.addWidget(self.settings['settings']['tempFolder'], stretch=1) self.settings['settings']['tempFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") ls.addRow(QtWidgets.QLabel("Folder for temporary files"), hbox_wrap) hbox_wrap = QtWidgets.QHBoxLayout() self.settings['settings']['outFolder'].setPlaceholderText( "drop folder here (will be created if not exists)") hbox_wrap.addWidget(self.settings['settings']['outFolder'], stretch=1) ls.addRow(QtWidgets.QLabel("Folder for output files"), hbox_wrap) ls.addRow(QtWidgets.QLabel("")) boxBtnRun = QtWidgets.QPushButton("Run selected steps now") boxBtnRun.clicked.connect(lambda: self.run_step("all")) boxBtnExp = QtWidgets.QPushButton( "Export selected steps to .bat") boxBtnExp.clicked.connect(self.createBatFile) # saveBtn = QtWidgets.QPushButton("Save to project file") # saveBtn.clicked.connect(self.save) boxVL.addWidget(boxBtnRun) boxVL.addWidget(boxBtnExp) # boxVL.addWidget(saveBtn) ls.addRow(boxRun) if name == "DTM": desc = QtWidgets.QLabel( "This first step will create a digital terrain model (DTM) from your point cloud data. " "Also, a shading of your DTM " "will be created for visualisation purposes. If the input file is not an ODM, one has to be " "created for the modelling process later on.") desc.setWordWrap(True) ls.addRow(desc) impmod, impscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsImport", "opalsImport", self.project, {'outFile': 'pointcloud.odm'}, ["inFile", "outFile"]) self.modules['dtmImp'] = impmod self.widgets['dtmImp'] = impscroll ls.addRow(impscroll) dtmmod, dtmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGrid", "opalsGrid", self.project, { 'interpolation': 'movingPlanes', 'gridSize': '1', 'outFile': 'DTM_1m.tif' }, [ "inFile", "outFile", "neighbours", "searchRadius", "interpolation" ]) self.modules['dtmGrid'] = dtmmod self.widgets['dtmGrid'] = dtmscroll dtmmod.afterRun = self.addDtm ls.addRow(dtmscroll) shdmod, shdscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsShade", "opalsShade", self.project, { 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_shd.tif' }, [ "inFile", "outFile", ]) self.modules['dtmShade'] = shdmod shdmod.afterRun = self.addShd ls.addRow(shdscroll) if name == "Slope": desc = QtWidgets.QLabel( "To automatically detect breaklines, a slope map is calculated. This map uses the neighboring 9" " pixels to estimate a plane. The gradient (steepest slope) is then taken, converted to a slope" "in degrees, and assigned to the pixel.") desc.setWordWrap(True) ls.addRow(desc) gfmod, gfscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsGridFeature", "opalsGridFeature", self.project, { 'feature': 'slpDeg', 'inFile': 'DTM_1m.tif', 'outFile': 'DTM_1m_slope.tif' }, ["inFile", "outFile", "feature"]) self.modules['slope'] = gfmod ls.addRow(gfscroll) if name == "2D-Approximation": desc = QtWidgets.QLabel( "The slope map is used to detect breaklines. For this, the algorithm by Canny (1986) is used.\n" "First, the slope map is convoluted with a gaussian kernel for smoothing, then the derivative " "is calculated. The two threshold parameters represent the upper and lower values for the " "binarization of the derivative map. Edges that have at least one pixel > upper threshold will be " "followed until they have a pixel < lower threshold.") desc.setWordWrap(True) ls.addRow(desc) edgeDmod, edgeDscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsEdgeDetect", "opalsEdgeDetect", self.project, { 'threshold': '2;4', 'sigmaSmooth': '1.8', 'inFile': 'DTM_1m_slope_slpDeg.tif', 'outFile': 'detected_edges.tif' }, ["inFile", "outFile", "threshold", "sigmaSmooth"]) self.modules['edgeDetect'] = edgeDmod ls.addRow(edgeDscroll) desc = QtWidgets.QLabel( "Since the output of opalsEdgeDetect is still a raster, we need to vectorize it:" ) desc.setWordWrap(True) ls.addRow(desc) vecmod, vecscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsVectorize", "opalsVectorize", self.project, { 'inFile': 'detected_edges.tif', 'outFile': 'detected_edges.shp' }, ["inFile", "outFile"]) self.modules['vectorize'] = vecmod ls.addRow(vecscroll) if name == "Topologic correction": desc = QtWidgets.QLabel( "Vectorized binary rasters usually need some topological cleaning. Here, this is done in three steps: \n" "1) Find the longest line and remove all lines < 10m\n" "2) Merge lines iteratively\n" "3) Clean up") desc.setWordWrap(True) ls.addRow(desc) lt1mod, lt1scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (1)", self.project, { 'method': 'longest', 'minLength': '10', 'snapRadius': '0', 'maxTol': '0.5', 'maxAngleDev': '75;15', 'avgDist': '3', 'inFile': 'detected_edges.shp', 'outFile': 'edges1.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt1'] = lt1mod ls.addRow(lt1scroll) lt2mod, lt2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (2)", self.project, { 'method': 'merge', 'minLength': '10', 'snapRadius': '3', 'maxTol': '0', 'maxAngleDev': '150;15', 'avgDist': '3', 'merge.minWeight': '0.75', 'merge.relWeightLead': '0', 'merge.maxIter': '10', 'merge.revertDist': '5', 'merge.revertInterval': '1', 'merge.searchGeneration': '4', 'merge.preventIntersection': '1', 'inFile': 'edges1.shp', 'outFile': 'edges2.shp' }, [ "inFile", "outFile", "method", "maxAngleDev", "snapRadius", "merge\..*" ]) lt2scroll.setFixedHeight(lt2scroll.height() - 200) self.modules['lt2'] = lt2mod ls.addRow(lt2scroll) lt3mod, lt3scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineTopology", "opalsLineTopology (3)", self.project, { 'method': 'longest', 'minLength': '25', 'snapRadius': '0', 'maxTol': '0', 'maxAngleDev': '90;15', 'avgDist': '3', 'inFile': 'edges2.shp', 'outFile': 'edges3.shp' }, ["inFile", "outFile", "method", "minLength", "maxTol"]) self.modules['lt3'] = lt3mod ls.addRow(lt3scroll) lt3mod.afterRun = self.add2DLines if name == "Editing": desc = QtWidgets.QLabel( "Please start editing the 2D approximations that have been loaded into qgis. Here are some tools " "that might help:") desc.setWordWrap(True) ls.addRow(desc) box1 = QtWidgets.QGroupBox("QuickLineModeller") from . import QpalsQuickLM self.quicklm = QpalsQuickLM.QpalsQuickLM( project=self.project, layerlist=self.layerlist, iface=self.iface) box1.setLayout(self.quicklm.fl) ls.addRow(box1) box2 = QtWidgets.QGroupBox("qpalsSection") from . import QpalsSection self.section = QpalsSection.QpalsSection( project=self.project, layerlist=self.layerlist, iface=self.iface) self.section.createWidget() box2.setLayout(self.section.ls) box2.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) ls.addRow(box2) if name == "3D-Modelling": desc = QtWidgets.QLabel( "The 2D approximations can now be used to model 3D breaklines in the pointcloud/the DTM." ) desc.setWordWrap(True) ls.addRow(desc) lmmod, lmscroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsLineModeler", "opalsLineModeler", self.project, { #"filter": "Class[Ground]", "approxFile": "edges3.shp", "outFile": "modelled_lines.shp" }, [ "inFile", "approxFile", "outFile", "filter", "patchLength", "patchWidth", "overlap", "angle", "minLength", "pointCount", "sigmaApriori" ]) self.modules['lm'] = lmmod ls.addRow(lmscroll) lmmod.afterRun = self.add3DLines if name == "Editing (3D)": desc = QtWidgets.QLabel( "Before exporting the final product, there are a few tools to check the " "quality of the result. This includes a topological check as well as a search" "for points that have a big height difference to the DTM - and might be erraneous." ) desc.setWordWrap(True) ls.addRow(desc) self.startQualityCheckBtn = QtWidgets.QPushButton( "Start calculation") self.startQualityCheckBtn.clicked.connect( self.runProblemSearchAsync) self.QualityCheckbar = QtWidgets.QProgressBar() self.QualityCheckDtm = QgsMapLayerComboBox() self.QualityCheckDtm.setFilters( QgsMapLayerProxyModel.RasterLayer) self.QualityCheckThreshold = QtWidgets.QLineEdit("0.5") ls.addRow( QtWidgets.QLabel("DTM Layer to compare heights with"), self.QualityCheckDtm) ls.addRow( QtWidgets.QLabel("Set height difference threshold [m]"), self.QualityCheckThreshold) hb = QtWidgets.QHBoxLayout() hb.addWidget(self.QualityCheckbar) hb.addWidget(self.startQualityCheckBtn) ls.addRow(hb) line = QtWidgets.QFrame() line.setFrameShape(QtWidgets.QFrame.HLine) line.setFrameShadow(QtWidgets.QFrame.Sunken) ls.addRow(line) self.editingls = ls self.edit3d_linelayerbox = QgsMapLayerComboBox() self.edit3d_linelayerbox.setFilters( QgsMapLayerProxyModel.LineLayer) self.edit3d_pointlayerbox = QgsMapLayerComboBox() self.edit3d_pointlayerbox.setFilters( QgsMapLayerProxyModel.PointLayer) self.edit3d_dtmlayerbox = QgsMapLayerComboBox() self.edit3d_dtmlayerbox.setFilters( QgsMapLayerProxyModel.RasterLayer) self.edit3d_pointlayerbox.currentIndexChanged.connect( self.nodeLayerChanged) self.edit3d_currPointId = QSpinBox() self.edit3d_currPointId.setMinimum(0) self.edit3d_currPointId.valueChanged.connect( self.showProblemPoint) ls.addRow("Select Line Layer:", self.edit3d_linelayerbox) ls.addRow("Select Problem Point layer:", self.edit3d_pointlayerbox) self.selectNodeBtn = QtWidgets.QPushButton("Next point") self.selectNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() + 1)) self.selectPrevNodeBtn = QtWidgets.QPushButton("Prev point") self.selectPrevNodeBtn.clicked.connect( lambda: self.edit3d_currPointId.setValue( self.edit3d_currPointId.value() - 1)) self.edit3d_countLabel = QtWidgets.QLabel() self.snapToDtmBtn = QtWidgets.QPushButton("Snap to:") self.snapToDtmBtn.clicked.connect(self.snapToDtm) self.remonveNodeBtn = QtWidgets.QPushButton("Remove") self.remonveNodeBtn.clicked.connect(self.removeNode) nextBox = QtWidgets.QHBoxLayout() nextBox.addWidget(QtWidgets.QLabel("Current point:")) nextBox.addWidget(self.edit3d_currPointId) nextBox.addWidget(QtWidgets.QLabel("/")) nextBox.addWidget(self.edit3d_countLabel) nextBox.addStretch() nextBox.addWidget(self.snapToDtmBtn) nextBox.addWidget(self.edit3d_dtmlayerbox) nextBox.addWidget(self.remonveNodeBtn) nextBox.addWidget(self.selectPrevNodeBtn) nextBox.addWidget(self.selectNodeBtn) ls.addRow(nextBox) self.nodeLayerChanged() if name == "Export": exp2mod, exp2scroll = QpalsModuleBase.QpalsModuleBase.createGroupBox( "opalsTranslate", "opalsTranslate", self.project, { 'oformat': 'shp', 'inFile': 'modelled_lines.shp', 'outFile': 'STRULI3D.shp', }, ["inFile", "outFile"]) self.modules['exp'] = exp2mod ls.addRow(exp2scroll) vl = QtWidgets.QVBoxLayout() vl.addLayout(ls, 1) navbar = QtWidgets.QHBoxLayout() next = QtWidgets.QPushButton("Next step >") next.clicked.connect(self.switchToNextTab) prev = QtWidgets.QPushButton("< Previous step") prev.clicked.connect(self.switchToPrevTab) runcurr = QtWidgets.QPushButton( "Run this step (all modules above)") runcurr.clicked.connect(lambda: self.run_step(None)) if idx > 0: navbar.addWidget(prev) navbar.addStretch() if name in [ "DTM", "Slope", "2D-Approximation", "Topologic correction", "3D-Modelling", "Export" ]: navbar.addWidget(runcurr) navbar.addStretch() if idx < len(self.names): navbar.addWidget(next) vl.addLayout(navbar) self.widgets[name].setLayout(vl) self.tabs.addTab(self.widgets[name], name) # set up connections self.tabs.currentChanged.connect(self.updateTabs) return self.scrollwidget def run_step(self, step_name=None): curridx = self.tabs.currentIndex() if step_name is None: step_name = self.names[curridx] modules = self.get_step_modules(step_name=step_name) self.multirunner = qMMR() for mod in modules: self.multirunner.add_module(mod, mod.updateBar, mod.run_async_finished, mod.errorBar) self.multirunner.start() def get_step_modules(self, step_name=None): steps_modules = { "DTM": [ self.modules['dtmImp'], self.modules['dtmGrid'], self.modules['dtmShade'] ], "Slope": [self.modules['slope']], "2D-Approximation": [self.modules['edgeDetect'], self.modules['vectorize']], "Topologic correction": [self.modules['lt1'], self.modules['lt2'], self.modules['lt3']], "3D-Modelling": [self.modules['lm']], "Export": [self.modules['exp']] } if step_name == "all": modules = [] if self.settings['settings']['chkDTM'].isChecked(): modules += steps_modules["DTM"] if self.settings['settings']['chkSlope'].isChecked(): modules += steps_modules["Slope"] if self.settings['settings']['chk2D'].isChecked(): modules += steps_modules["2D-Approximation"] if self.settings['settings']['chktopo2D'].isChecked(): modules += steps_modules["Topologic correction"] if self.settings['settings']['chk3Dmodel'].isChecked(): modules += steps_modules["3D-Modelling"] if self.settings['settings']['chkExport'].isChecked(): modules += steps_modules["Export"] else: modules = steps_modules[step_name] return modules def createBatFile(self): saveTo = QtWidgets.QFileDialog.getSaveFileName(None, caption='Save to file') try: f = open(saveTo, 'w') f.write("rem BATCH FILE CREATED WITH QPALS\r\n") modules = self.get_step_modules("all") for module in modules: f.write(str(module) + "\r\n") f.close() except Exception as e: raise Exception("Saving to batch failed.", e) def updateBar(self, message): out_lines = [item for item in re.split("[\n\r\b]", message) if item] percentage = out_lines[-1] # print percentage if r"%" in percentage: perc = QpalsModuleBase.get_percentage(percentage) self.secInst.progress.setValue(int(perc)) def addDtm(self): file = self.modules['dtmGrid'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addRasterLayer(file, "DTM") def addShd(self): file = self.modules['dtmShade'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addRasterLayer(file, "DTM-Shading") def add2DLines(self): file = self.modules['lt3'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addVectorLayer(file, "2D-Approximations", "ogr") def add3DLines(self): file = self.modules['lm'].getParam('outFile').val if not os.path.isabs(file): file = os.path.join(self.project.workdir, file) self.iface.addVectorLayer(file, "3D Modelled Lines", "ogr") def runProblemSearchAsync(self): linefile = self.modules['lm'].getParam('outFile').val if not os.path.isabs(linefile): linefile = os.path.join(self.project.workdir, linefile) dtm = self.QualityCheckDtm.currentLayer() dtm_thres = self.QualityCheckThreshold.text() try: dtm_thres = float(dtm_thres) except: dtm_thres = 0 self.QualityWorker = findDoubleSegments.RunWorker( linefile, os.path.join(self.project.workdir, "quality"), dtm, dtm_thres, 50, 1000) self.QualityWorker.progress.connect(self.updateQualityBar) self.QualityWorker.finished.connect(self.QualityFinished) self.QualityWorker.error.connect(self.problemSearchError) self.QualityThread = QtCore.QThread() self.QualityWorker.moveToThread(self.QualityThread) self.QualityThread.started.connect(self.QualityWorker.run) self.QualityThread.start() self.startQualityCheckBtn.setEnabled(False) self.startQualityCheckBtn.setText("processing...") def problemSearchError(self, err): (msg, e, mod) = err print(msg) def updateQualityBar(self, fl): self.QualityCheckbar.setValue(fl) def QualityFinished(self): self.startQualityCheckBtn.setEnabled(True) self.startQualityCheckBtn.setText("Start calculation") self.updateQualityBar(100) file = os.path.join(self.project.workdir, "quality", "problems.shp") self.iface.addVectorLayer(file, "Problems", "ogr") # def save(self): # import cPickle # proj = QgsProject.instance() # tabs = cPickle.dumps(self.tabs, -1) # proj.writeEntry("qpals", "linemodelerinst", tabs) # print tabs def close(self): # remove rubber band if present if self.section.ltool.rb: self.section.ltool.canvas.scene().removeItem(self.section.ltool.rb) self.iface.actionPan().trigger()
def getSpinBoxIteration(wgt, value): sp = QSpinBox(wgt) sp.setRange(0, 3) sp.setSingleStep(1) sp.setValue(value) return sp
class QvFormNovaMapificacio(QvFormBaseMapificacio): def __init__(self, llegenda, amplada=500, mapificacio=None, simple=True): super().__init__(llegenda, amplada) self.fCSV = mapificacio self.simple = simple self.taulaMostra = None self.setWindowTitle('Afegir capa amb mapa simbòlic') self.layout = QVBoxLayout() self.layout.setSpacing(14) self.setLayout(self.layout) if self.fCSV is None: self.arxiu = QgsFileWidget() self.arxiu.setStorageMode(QgsFileWidget.GetFile) self.arxiu.setDialogTitle('Selecciona fitxer de dades…') self.arxiu.setDefaultRoot(RUTA_LOCAL) self.arxiu.setFilter('Arxius CSV (*.csv)') self.arxiu.setSelectedFilter('Arxius CSV (*.csv)') self.arxiu.lineEdit().setReadOnly(True) self.arxiu.fileChanged.connect(self.arxiuSeleccionat) self.zona = QComboBox(self) self.zona.setEditable(False) self.zona.addItem('Selecciona zona…') self.zona.currentIndexChanged.connect(self.canviaZona) self.mapa = QComboBox(self) self.mapa.setEditable(False) self.mapa.setIconSize(QSize(126, 126)) self.mapa.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.mapa.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Àrees.PNG')), 'Àrees') self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Cercles.PNG')), 'Cercles') self.capa = QLineEdit(self) self.capa.setMaxLength(40) self.tipus = QComboBox(self) self.tipus.setEditable(False) self.tipus.addItem('Selecciona tipus…') self.tipus.addItems(mv.MAP_AGREGACIO.keys()) self.tipus.currentIndexChanged.connect(self.canviaTipus) self.distribucio = QComboBox(self) self.distribucio.setEditable(False) self.distribucio.addItem(next(iter(mv.MAP_DISTRIBUCIO.keys()))) self.calcul = QvComboBoxCamps(self) self.filtre = QvComboBoxCamps(self, multiple=True) self.color = QComboBox(self) self.color.setEditable(False) self.comboColors(self.color) self.metode = QComboBox(self) self.metode.setEditable(False) self.metode.addItems(mv.MAP_METODES.keys()) self.intervals = QSpinBox(self) self.intervals.setMinimum(2) self.intervals.setMaximum(mv.MAP_MAX_CATEGORIES) self.intervals.setSingleStep(1) self.intervals.setValue(4) self.intervals.setSuffix(" (depèn del mètode)") # self.intervals.valueChanged.connect(self.deselectValue) self.bTaula = QPushButton('Veure arxiu') self.bTaula.setEnabled(False) self.bTaula.clicked.connect(self.veureArxiu) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.cancel) self.buttons.addButton(self.bTaula, QDialogButtonBox.ResetRole) self.gDades = QGroupBox('Agregació de dades') self.lDades = QFormLayout() self.lDades.setSpacing(14) self.gDades.setLayout(self.lDades) if self.fCSV is None: self.lDades.addRow('Arxiu de dades:', self.arxiu) self.lDades.addRow('Zona:', self.zona) self.lDades.addRow("Tipus d'agregació:", self.tipus) self.lDades.addRow('Camp de càlcul:', self.calcul) if self.simple: self.filtre.setVisible(False) self.distribucio.setVisible(False) else: self.lDades.addRow('Filtre:', self.filtre) self.lDades.addRow('Distribució:', self.distribucio) self.gMapa = QGroupBox('Definició del mapa simbòlic') self.lMapa = QFormLayout() self.lMapa.setSpacing(14) self.gMapa.setLayout(self.lMapa) self.lMapa.addRow('Nom de capa:', self.capa) self.lMapa.addRow('Tipus de mapa:', self.mapa) self.gSimb = QGroupBox('Simbologia del mapa') self.lSimb = QFormLayout() self.lSimb.setSpacing(14) self.gSimb.setLayout(self.lSimb) self.lSimb.addRow('Color base:', self.color) self.lSimb.addRow('Mètode classificació:', self.metode) self.lSimb.addRow("Nombre d'intervals:", self.intervals) self.layout.addWidget(self.gDades) self.layout.addWidget(self.gMapa) if self.simple: self.gSimb.setVisible(False) else: self.layout.addWidget(self.gSimb) self.layout.addWidget(self.buttons) self.adjustSize() self.nouArxiu() def exec(self): # La mapificación solo funciona si está instalado el módulo pandas if PANDAS_ENABLED: return super().exec() else: self.msgError(PANDAS_ERROR) return QDialog.Rejected @pyqtSlot() def veureArxiu(self): if self.taulaMostra is not None: self.taulaMostra.show() self.taulaMostra.activateWindow() def campsDB(self, nom): res = [] if nom != '': fich = RUTA_DADES + mv.MAP_ZONES_DB if os.path.isfile(fich): conn = sqlite3.connect('file:' + fich + '?mode=ro', uri=True) conn.row_factory = sqlite3.Row c = conn.cursor() c.execute('select * from ' + nom) # nom.split('.')[0]) row = c.fetchone() # res = [i[0].upper() for i in c.description] res = [i.upper() for i in row.keys()] conn.close() return res def soloPrimerItem(self, combo): combo.setCurrentIndex(0) ultimo = combo.count() - 1 for n in range(ultimo, 0, -1): combo.removeItem(n) @pyqtSlot() def canviaZona(self): self.distribucio.setCurrentIndex(0) self.soloPrimerItem(self.distribucio) if self.zona.currentIndex() > 0: z = self.zona.currentText() campsZona = self.campsDB(mv.MAP_ZONES[z][1]) # Carga combo con distribuciones si el campo correspondiente está en la BBDD for dist, campo in mv.MAP_DISTRIBUCIO.items(): if campo != '' and campo in campsZona: self.distribucio.addItem(dist) @pyqtSlot() def canviaTipus(self): if self.tipus.currentText() == 'Recompte': self.calcul.setCurrentIndex(-1) self.calcul.setEnabled(False) else: self.calcul.setEnabled(True) def borrarArxiu(self): if self.taulaMostra is not None: self.taulaMostra.hide() self.taulaMostra = None self.bTaula.setEnabled(False) self.tipus.setCurrentIndex(0) self.soloPrimerItem(self.zona) self.calcul.clear() self.filtre.clear() def nouArxiu(self): if self.fCSV is None: return # Carga combo con zonas si el campo correspondiente está en el fichero CSV num = 0 for zona, val in mv.MAP_ZONES.items(): if val[1] != '' and self.fCSV.prefixe + QvSqlite.getAlias(val[0]) in self.fCSV.camps: self.zona.addItem(zona) num = num + 1 # Comprobar si la extensión del mapa está limitada if num > 0: extensio = self.fCSV.testExtensioArxiu(mv.MAP_EXTENSIO) if extensio: # Mapa limitado self.comboDelete(self.zona, mv.MAP_TRUE_EXTENSIO) else: # Mapa completo self.comboDelete(self.zona, mv.MAP_FALSE_EXTENSIO) # Ajustar combo de zonas if num == 0: self.msgInfo("El fitxer " + self.fCSV.fZones + " no té cap camp de zona") if hasattr(self, 'arxiu'): self.arxiu.lineEdit().clear() self.arxiu.setFocus() return if num == 1: self.zona.setCurrentIndex(1) self.capa.setFocus() else: self.zona.setFocus() self.taulaMostra = QvEditorCsv(self.fCSV.fZones, [], 'utf-8', self.fCSV.separador, self) self.taulaMostra.setWindowTitle("Vista prèvia d'arxiu geocodificat") self.taulaMostra.setReadOnly(True) self.bTaula.setEnabled(True) self.calcul.setItems(self.fCSV.camps, primer='') self.filtre.setItems(self.fCSV.camps) @pyqtSlot(str) def arxiuSeleccionat(self, nom): if nom == '': return self.borrarArxiu() self.fCSV = QvMapificacio(nom) self.nouArxiu() def validaSortida(self, nom): fSalida = self.fCSV.nomArxiuSortida(self.fCSV.netejaString(nom, True)) return self.msgSobreescriure(fSalida) def valida(self): ok = False if hasattr(self, 'arxiu') and self.arxiu.filePath() == '': self.msgInfo("S'ha de seleccionar un arxiu de dades") self.arxiu.setFocus() elif self.zona.currentIndex() <= 0: self.msgInfo("S'ha de seleccionar una zona") self.zona.setFocus() elif self.capa.text().strip() == '': self.msgInfo("S'ha de introduir un nom de capa") self.capa.setFocus() elif self.tipus.currentIndex() <= 0: self.msgInfo("S'ha de seleccionar un tipus d'agregació") self.tipus.setFocus() elif self.calcul.currentText().strip() == '' and self.tipus.currentText() != 'Recompte': self.msgInfo("S'ha de introduir un cálcul per fer l'agregació") self.calcul.setFocus() elif self.fCSV is None: return self.msgInfo("No hi ha cap fitxer seleccionat") elif not self.validaSortida(self.capa.text().strip()): self.capa.setFocus() else: ok = True return ok def setRenderParams(self): self.renderParams = QvMapRendererParams(self.mapa.currentText()) if self.simple: self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase] else: self.renderParams.colorBase = mv.MAP_COLORS[self.color.currentText()] if self.renderParams.colorContorn is None or self.renderParams.colorContorn == 'Base': self.renderParams.colorContorn = self.renderParams.colorBase else: self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn] if self.tipus.currentText().startswith('Recompte') and \ self.distribucio.currentText() == "Total": self.renderParams.numDecimals = 0 else: self.renderParams.numDecimals = 2 if self.renderParams.tipusMapa == 'Àrees': self.renderParams.modeCategories = mv.MAP_METODES[self.metode.currentText()] self.renderParams.numCategories = self.intervals.value() if self.renderParams.tipusMapa == 'Cercles': zona = self.zona.currentText() if zona == 'Districte': self.renderParams.increase = 8 elif zona == 'Barri': self.renderParams.increase = 4 elif zona == 'Àrea estadística bàsica': self.renderParams.increase = 3 elif zona == 'Secció censal': self.renderParams.increase = 2 else: self.renderParams.increase = 1 def procesa(self): if self.taulaMostra is not None: self.taulaMostra.hide() self.setRenderParams() ok = self.fCSV.agregacio(self.llegenda, self.capa.text().strip(), self.zona.currentText(), self.tipus.currentText(), self.renderParams, campAgregat=self.calcul.currentText().strip(), simple=self.simple, filtre=self.filtre.currentText().strip(), tipusDistribucio=self.distribucio.currentText(), form=self) if ok: return '' else: return self.fCSV.msgError
def getSpinBoxAzimuth(wgt, value): sp = QSpinBox( wgt) sp.setRange(0, 45) sp.setSingleStep(1) sp.setSuffix(' degrees') sp.setValue(value) msg = QCoreApplication.translate('GimpSelectionFeature', 'Degrees of azimuth between vertexs') sp.setToolTip( msg ) return sp
def __init__(self, llegenda, amplada=500, mapificacio=None, simple=True): super().__init__(llegenda, amplada) self.fCSV = mapificacio self.simple = simple self.taulaMostra = None self.setWindowTitle('Afegir capa amb mapa simbòlic') self.layout = QVBoxLayout() self.layout.setSpacing(14) self.setLayout(self.layout) if self.fCSV is None: self.arxiu = QgsFileWidget() self.arxiu.setStorageMode(QgsFileWidget.GetFile) self.arxiu.setDialogTitle('Selecciona fitxer de dades…') self.arxiu.setDefaultRoot(RUTA_LOCAL) self.arxiu.setFilter('Arxius CSV (*.csv)') self.arxiu.setSelectedFilter('Arxius CSV (*.csv)') self.arxiu.lineEdit().setReadOnly(True) self.arxiu.fileChanged.connect(self.arxiuSeleccionat) self.zona = QComboBox(self) self.zona.setEditable(False) self.zona.addItem('Selecciona zona…') self.zona.currentIndexChanged.connect(self.canviaZona) self.mapa = QComboBox(self) self.mapa.setEditable(False) self.mapa.setIconSize(QSize(126, 126)) self.mapa.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.mapa.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Àrees.PNG')), 'Àrees') self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Cercles.PNG')), 'Cercles') self.capa = QLineEdit(self) self.capa.setMaxLength(40) self.tipus = QComboBox(self) self.tipus.setEditable(False) self.tipus.addItem('Selecciona tipus…') self.tipus.addItems(mv.MAP_AGREGACIO.keys()) self.tipus.currentIndexChanged.connect(self.canviaTipus) self.distribucio = QComboBox(self) self.distribucio.setEditable(False) self.distribucio.addItem(next(iter(mv.MAP_DISTRIBUCIO.keys()))) self.calcul = QvComboBoxCamps(self) self.filtre = QvComboBoxCamps(self, multiple=True) self.color = QComboBox(self) self.color.setEditable(False) self.comboColors(self.color) self.metode = QComboBox(self) self.metode.setEditable(False) self.metode.addItems(mv.MAP_METODES.keys()) self.intervals = QSpinBox(self) self.intervals.setMinimum(2) self.intervals.setMaximum(mv.MAP_MAX_CATEGORIES) self.intervals.setSingleStep(1) self.intervals.setValue(4) self.intervals.setSuffix(" (depèn del mètode)") # self.intervals.valueChanged.connect(self.deselectValue) self.bTaula = QPushButton('Veure arxiu') self.bTaula.setEnabled(False) self.bTaula.clicked.connect(self.veureArxiu) self.buttons = QDialogButtonBox() self.buttons.addButton(QDialogButtonBox.Ok) self.buttons.accepted.connect(self.accept) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.cancel) self.buttons.addButton(self.bTaula, QDialogButtonBox.ResetRole) self.gDades = QGroupBox('Agregació de dades') self.lDades = QFormLayout() self.lDades.setSpacing(14) self.gDades.setLayout(self.lDades) if self.fCSV is None: self.lDades.addRow('Arxiu de dades:', self.arxiu) self.lDades.addRow('Zona:', self.zona) self.lDades.addRow("Tipus d'agregació:", self.tipus) self.lDades.addRow('Camp de càlcul:', self.calcul) if self.simple: self.filtre.setVisible(False) self.distribucio.setVisible(False) else: self.lDades.addRow('Filtre:', self.filtre) self.lDades.addRow('Distribució:', self.distribucio) self.gMapa = QGroupBox('Definició del mapa simbòlic') self.lMapa = QFormLayout() self.lMapa.setSpacing(14) self.gMapa.setLayout(self.lMapa) self.lMapa.addRow('Nom de capa:', self.capa) self.lMapa.addRow('Tipus de mapa:', self.mapa) self.gSimb = QGroupBox('Simbologia del mapa') self.lSimb = QFormLayout() self.lSimb.setSpacing(14) self.gSimb.setLayout(self.lSimb) self.lSimb.addRow('Color base:', self.color) self.lSimb.addRow('Mètode classificació:', self.metode) self.lSimb.addRow("Nombre d'intervals:", self.intervals) self.layout.addWidget(self.gDades) self.layout.addWidget(self.gMapa) if self.simple: self.gSimb.setVisible(False) else: self.layout.addWidget(self.gSimb) self.layout.addWidget(self.buttons) self.adjustSize() self.nouArxiu()
def create_eid_selector(self): self.eid_lbl = QLabel('Event ID') self.eid_sbx = QSpinBox() self.eid_sbx.setEnabled(False) self.vlayout.addWidget(self.eid_lbl) self.vlayout.addWidget(self.eid_sbx)
def __init__(self, parent=None): # pylint: disable=too-many-statements super().__init__(parent) self.setWindowTitle(self.tr('Redistrict Plugin | Settings')) layout = QVBoxLayout() self.auth_label = QLabel(self.tr('Authentication configuration')) layout.addWidget(self.auth_label) self.auth_value = QgsAuthConfigSelect() layout.addWidget(self.auth_value) auth_id = get_auth_config_id() if auth_id: self.auth_value.setConfigId(auth_id) layout.addWidget(QLabel(self.tr('API base URL'))) self.base_url_edit = QLineEdit() self.base_url_edit.setText(QgsSettings().value('redistrict/base_url', '', str, QgsSettings.Plugins)) layout.addWidget(self.base_url_edit) h_layout = QHBoxLayout() h_layout.addWidget(QLabel(self.tr('Check for completed requests every'))) self.check_every_spin = QSpinBox() self.check_every_spin.setMinimum(10) self.check_every_spin.setMaximum(600) self.check_every_spin.setSuffix(' ' + self.tr('s')) self.check_every_spin.setValue(QgsSettings().value('redistrict/check_every', '30', int, QgsSettings.Plugins)) h_layout.addWidget(self.check_every_spin) layout.addLayout(h_layout) self.use_mock_checkbox = QCheckBox(self.tr('Use mock Statistics NZ API')) self.use_mock_checkbox.setChecked(get_use_mock_api()) layout.addWidget(self.use_mock_checkbox) self.test_button = QPushButton(self.tr('Test API connection')) self.test_button.clicked.connect(self.test_api) layout.addWidget(self.test_button) self.use_overlays_checkbox = QCheckBox(self.tr('Show updated populations during interactive redistricting')) self.use_overlays_checkbox.setChecked( QgsSettings().value('redistrict/show_overlays', False, bool, QgsSettings.Plugins)) layout.addWidget(self.use_overlays_checkbox) self.use_sound_group_box = QGroupBox(self.tr('Use audio feedback')) self.use_sound_group_box.setCheckable(True) self.use_sound_group_box.setChecked( QgsSettings().value('redistrict/use_audio_feedback', False, bool, QgsSettings.Plugins)) sound_layout = QGridLayout() sound_layout.addWidget(QLabel(self.tr('When meshblock redistricted')), 0, 0) self.on_redistrict_file_widget = QgsFileWidget() self.on_redistrict_file_widget.setDialogTitle(self.tr('Select Audio File')) self.on_redistrict_file_widget.setStorageMode(QgsFileWidget.GetFile) self.on_redistrict_file_widget.setFilePath( QgsSettings().value('redistrict/on_redistrict', '', str, QgsSettings.Plugins)) self.on_redistrict_file_widget.setFilter(self.tr('Wave files (*.wav *.WAV)')) sound_layout.addWidget(self.on_redistrict_file_widget, 0, 1) self.play_on_redistrict_sound_button = QPushButton(self.tr('Test')) self.play_on_redistrict_sound_button.clicked.connect(self.play_on_redistrict_sound) sound_layout.addWidget(self.play_on_redistrict_sound_button, 0, 2) self.use_sound_group_box.setLayout(sound_layout) layout.addWidget(self.use_sound_group_box) button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) layout.addWidget(button_box) button_box.rejected.connect(self.reject) button_box.accepted.connect(self.accept) self.setLayout(layout)