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
Beispiel #3
0
    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
Beispiel #4
0
 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
Beispiel #5
0
 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
Beispiel #6
0
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
Beispiel #7
0
    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())
        )
Beispiel #10
0
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>&middot;&nbsp;{}</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))
Beispiel #11
0
    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
Beispiel #13
0
    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()
Beispiel #16
0
        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_())
Beispiel #17
0
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
Beispiel #18
0
    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()
Beispiel #20
0
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
Beispiel #22
0
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
Beispiel #24
0
    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)
Beispiel #26
0
    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)