Ejemplo n.º 1
0
class SpinBox(InputType):
    '''
    spinbox integer number input
    '''
    InputClass = QSpinBox
    def __init__(self, minimum=0, maximum=100000000, step=1,
                 lockable=False, locked=False, reversed_lock=False, **kwargs):
        '''
        Parameters
        ----------
        minimum : int, optional
            minimum value that the user can set
        maximum : int, optional
            maximum value that the user can set
        step : int, optional
            the single step of the number input when changing values,
            defaults to 1
        lockable : bool, optional
            the number input can be locked by a checkbox that will
            be displayed next to it if True, defaults to not lockable
        locked : bool, optional
            initial lock-state of input, only applied if lockable is True,
            defaults to input being not locked
        reversed_lock : bool, optional
            reverses the locking logic, if True checking the lock will enable
            the inputs instead of disabling them, defaults to normal lock
            behaviour (disabling inputs when setting lock-state to True)
        '''
        super().__init__(**kwargs)
        self.minimum = minimum
        self.maximum = maximum
        self.input = self.InputClass()
        self.input.setMinimum(minimum)
        self.input.setMaximum(maximum)
        self.input.setSingleStep(step)
        self.input.valueChanged.connect(self.changed.emit)
        self.registerFocusEvent(self.input)
        self.lockable = lockable

        # ToDo: almost the same as in Slider, outsource into common function
        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.input.setEnabled(is_locked if reversed_lock else
                                      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))

    def set_value(self, value: int):
        '''
        set the value of the input

        Parameters
        ----------
        value : int
            value to set
        '''
        self.input.setValue(value or 0)

    def get_value(self) -> int:
        '''
        get the current value of the input

        Returns
        -------
        value : int
            current value of input
        '''
        return self.input.value()

    @property
    def is_locked(self) -> bool:
        '''
        Returns
        -------
        bool
            current lock-state of number input
        '''
        if not self.lockable:
            return False
        return self.lock_button.isChecked()

    def draw(self, layout: QLayout, unit: str = ''):
        '''
        add number input and the lock (if lockable) 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.input)
        if unit:
            l.addWidget(QLabel(unit))
        if self.lockable:
            l.addWidget(self.lock_button)
        layout.addLayout(l)
class LayerTreeImageLegendWidget(QWidget):
    """
    Layer tree widget for displaying image legend in a raster layer
    """
    def __init__(self, layer):
        super().__init__()
        self.layer = layer
        # Is legend a png file?
        self.my_img = os.path.splitext(layer.source())[0] + '.legend.png'
        if not os.path.exists(self.my_img):
            # No, is it a jpg?
            self.my_img = os.path.splitext(layer.source())[0] + '.legend.jpg'
            if not os.path.exists(self.my_img):
                # No: is this raster using a common legend?
                pwd = os.getcwd()
                compath = os.path.split(layer.source())[0]
                os.chdir(compath)
                englob = glob.glob("*.legendcommon.*")
                if len(englob) > 0:
                    # Yes
                    self.my_img = os.path.join(compath, self.getMy_Img())
                else:
                    # No: abort
                    os.chdir(pwd)
                    return
                #
                os.chdir(pwd)

        im = Image.open(self.my_img)
        w, h = im.size

        self.setAutoFillBackground(False)
        self.my_pix = QPixmap(self.my_img)
        self.imgleg = QPushButton()
        self.imgleg.setIcon(QIcon(self.my_pix))
        self.imgleg.setCheckable(False)
        self.imgleg.setFlat(True)
        if w >= h:
            self.imgleg.setIconSize(QSize(200, int(200 * h / w)))
        else:
            self.imgleg.setIconSize(QSize(int(200 * w / h), 200))
        self.imgleg.setSizePolicy(QSizePolicy.MinimumExpanding,
                                  QSizePolicy.MinimumExpanding)

        layout = QHBoxLayout()
        spacer = QSpacerItem(1, 0, QSizePolicy.MinimumExpanding,
                             QSizePolicy.Minimum)
        layout.addWidget(self.imgleg)
        layout.addItem(spacer)
        self.setLayout(layout)

        if self.layer.type() == QgsMapLayer.RasterLayer:
            self.imgleg.released.connect(self.showLegend)

    def showLegend(self):
        """
        Triggered when the image legend button is released after click/press.
        """
        webbrowser.open('file:///' + self.my_img)

    def getMy_Img(self):
        """
        Find and return the common legend file.
           Can only be 1 common legend per folder.
        """
        qq = map(glob.glob, ["*.legendcommon.png", "*.legendcommon.jpg"])
        s = ''
        for q in qq:
            try:
                # qq is made of two lists, one should be empty the other one should only
                #  have one element: the common legend
                # If empty list then assignment will cause an error, so at the end only
                #  one element is stored in s
                s = q[0]
            except:
                pass
        return s
Ejemplo n.º 3
0
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()
class MapAnimator(QWidget):
    """
    todo:
    - make active button toggle between states
    - enable/ disable pulldowns on activate
    - on enable, copy current model layers to memory and add column
    - add listener to slider - set current values in layer
    - add listeners to pulldown -
    - add listeners to result selection switch (ask if ok)
    - styling for layers

    """

    def __init__(self, parent, iface, root_tool):

        super().__init__(parent)
        self.iface = iface
        self.root_tool = root_tool
        self.node_parameters = {}
        self.line_parameters = {}
        self.current_node_parameter = None
        self.current_line_parameter = None
        self.node_layer = None
        self.line_layer = None
        self.line_layer_groundwater = None
        self.node_layer_groundwater = None
        self.state = False
        self.setup_ui()

        # set initial state
        self.line_parameter_combo_box.setEnabled(False)
        self.node_parameter_combo_box.setEnabled(False)

        # connect to signals
        self.activateButton.clicked.connect(self.set_activation_state)

        self.state_connectiong_set = False

        self.line_parameter_combo_box.currentIndexChanged.connect(
            self.on_line_parameter_change
        )
        self.node_parameter_combo_box.currentIndexChanged.connect(
            self.on_node_parameter_change
        )

        self.root_tool.timeslider_widget.datasource_changed.connect(
            self.on_active_ts_datasource_change
        )

        self.on_active_ts_datasource_change()

    def on_line_parameter_change(self):
        old_parameter = self.current_line_parameter

        self.current_line_parameter = self.line_parameters[
            self.line_parameter_combo_box.currentText()
        ]

        if old_parameter != self.current_line_parameter:
            self.update_results()

    def on_node_parameter_change(self):
        old_parameter = self.current_node_parameter
        self.current_node_parameter = self.node_parameters[
            self.node_parameter_combo_box.currentText()
        ]

        if old_parameter != self.current_node_parameter:
            self.update_results()

    def on_active_ts_datasource_change(self):
        # reset
        parameter_config = self._get_active_parameter_config()

        for combo_box, parameters, pc in (
            (
                self.line_parameter_combo_box,
                self.line_parameters,
                parameter_config["q"],
            ),
            (
                self.node_parameter_combo_box,
                self.node_parameters,
                parameter_config["h"],
            ),
        ):

            nr_old_parameters = combo_box.count()

            parameters.update(dict([(p["name"], p) for p in pc]))

            combo_box.insertItems(0, [p["name"] for p in pc])

            # todo: find best matching parameter based on previous selection
            if nr_old_parameters > 0:
                combo_box.setCurrentIndex(0)

            nr_parameters_tot = combo_box.count()
            for i in reversed(
                list(range(nr_parameters_tot - nr_old_parameters, nr_parameters_tot))
            ):
                combo_box.removeItem(i)

    def _get_active_parameter_config(self):

        active_ts_datasource = self.root_tool.timeslider_widget.active_ts_datasource

        if active_ts_datasource is not None:
            # TODO: just taking the first datasource, not sure if correct:
            threedi_result = active_ts_datasource.threedi_result()
            available_subgrid_vars = threedi_result.available_subgrid_map_vars
            # Make a deepcopy because we don't want to change the cached variables
            # in threedi_result.available_subgrid_map_vars
            available_subgrid_vars = copy.deepcopy(available_subgrid_vars)
            # 'q_pump' is a special case, which is currently not supported in the
            # animation tool.
            if "q_pump" in available_subgrid_vars:
                available_subgrid_vars.remove("q_pump")
            # TimesliderWidget of the map_animator does not yet support variables of
            # the aggregate netcdf, thus we do not display those variables.
            parameter_config = generate_parameter_config(
                available_subgrid_vars, agg_vars=[]
            )
        else:
            parameter_config = {"q": {}, "h": {}}

        return parameter_config

    def set_activation_state(self, state):
        self.state = self.activateButton.isChecked()

        if state:
            if self.root_tool.ts_datasources.rowCount() > 0:
                self.line_parameter_combo_box.setEnabled(True)
                self.node_parameter_combo_box.setEnabled(True)
                self.prepare_animation_layers()
                self.root_tool.timeslider_widget.sliderReleased.connect(
                    self.update_results
                )

            # add listeners
            self.state_connection_set = True
        else:
            self.line_parameter_combo_box.setEnabled(False)
            self.node_parameter_combo_box.setEnabled(False)

            if self.state_connection_set:
                # remove listeners
                self.state_connection_set = False

    def prepare_animation_layers(self):

        result = self.root_tool.timeslider_widget.active_ts_datasource

        if result is None:
            # todo: logger warning
            return

        if self.node_layer is not None:
            # todo: react on datasource change
            return

        line, node, pump = result.get_result_layers()

        # lines without groundwater results
        self.line_layer = copy_layer_into_memory_layer(line, "line_results")
        self.line_layer.dataProvider().addAttributes(
            [QgsField("result", QVariant.Double)]
        )
        features = self.line_layer.getFeatures()
        ids = [
            f.id()
            for f in features
            if f.attribute("type") == "2d_groundwater"
            or f.attribute("type") == "1d_2d_groundwater"
        ]
        self.line_layer.dataProvider().deleteFeatures(ids)
        self.line_layer.updateFields()

        # lines with groundwater results
        self.line_layer_groundwater = copy_layer_into_memory_layer(
            line, "line_results_groundwater"
        )
        self.line_layer_groundwater.dataProvider().addAttributes(
            [QgsField("result", QVariant.Double)]
        )
        features = self.line_layer_groundwater.getFeatures()
        ids = [
            f.id()
            for f in features
            if f.attribute("type") != "2d_groundwater"
            and f.attribute("type") != "1d_2d_groundwater"
        ]
        self.line_layer_groundwater.dataProvider().deleteFeatures(ids)
        self.line_layer_groundwater.updateFields()

        # nodes without groundwater results
        self.node_layer = copy_layer_into_memory_layer(node, "node_results")
        self.node_layer.dataProvider().addAttributes(
            [QgsField("result", QVariant.Double)]
        )
        features = self.node_layer.getFeatures()
        ids = [
            f.id()
            for f in features
            if f.attribute("type") == "2d_groundwater"
            or f.attribute("type") == "2d_groundwater_bound"
        ]
        self.node_layer.dataProvider().deleteFeatures(ids)
        self.node_layer.updateFields()

        # nodes with groundwater results
        self.node_layer_groundwater = copy_layer_into_memory_layer(
            node, "node_results_groundwater"
        )
        self.node_layer_groundwater.dataProvider().addAttributes(
            [QgsField("result", QVariant.Double)]
        )
        features = self.node_layer_groundwater.getFeatures()
        ids = [
            f.id()
            for f in features
            if f.attribute("type") != "2d_groundwater"
            and f.attribute("type") != "2d_groundwater_bound"
        ]
        self.node_layer_groundwater.dataProvider().deleteFeatures(ids)
        self.node_layer_groundwater.updateFields()

        # todo: add this layers to the correct location
        self.line_layer.loadNamedStyle(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                os.path.pardir,
                "layer_styles",
                "tools",
                "line_discharge.qml",
            )
        )

        self.line_layer_groundwater.loadNamedStyle(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                os.path.pardir,
                "layer_styles",
                "tools",
                "line_groundwater_velocity.qml",
            )
        )

        self.node_layer.loadNamedStyle(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                os.path.pardir,
                "layer_styles",
                "tools",
                "node_waterlevel_diff.qml",
            )
        )

        self.node_layer_groundwater.loadNamedStyle(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                os.path.pardir,
                "layer_styles",
                "tools",
                "node_groundwaterlevel_diff.qml",
            )
        )

        root = QgsProject.instance().layerTreeRoot()

        animation_group_name = "animation_layers"
        animation_group = root.findGroup(animation_group_name)
        if animation_group is None:
            animation_group = root.insertGroup(0, animation_group_name)
        animation_group.removeAllChildren()

        QgsProject.instance().addMapLayer(self.line_layer, False)
        QgsProject.instance().addMapLayer(self.line_layer_groundwater, False)
        QgsProject.instance().addMapLayer(self.node_layer, False)
        QgsProject.instance().addMapLayer(self.node_layer_groundwater, False)

        animation_group.insertLayer(0, self.line_layer)
        animation_group.insertLayer(1, self.line_layer_groundwater)
        animation_group.insertLayer(2, self.node_layer)
        animation_group.insertLayer(3, self.node_layer_groundwater)

    def update_results(self):
        if not self.state:
            return

        result = self.root_tool.timeslider_widget.active_ts_datasource

        timestep_nr = self.root_tool.timeslider_widget.value()

        threedi_result = result.threedi_result()

        for layer, parameter, stat in (
            (self.node_layer, self.current_node_parameter["parameters"], "diff"),
            (self.line_layer, self.current_line_parameter["parameters"], "act"),
            (
                self.node_layer_groundwater,
                self.current_node_parameter["parameters"],
                "diff",
            ),
            (
                self.line_layer_groundwater,
                self.current_line_parameter["parameters"],
                "act",
            ),
        ):  # updated to act for actual, display actual value

            provider = layer.dataProvider()

            values = threedi_result.get_values_by_timestep_nr(parameter, timestep_nr)
            if isinstance(values, np.ma.MaskedArray):
                values = values.filled(np.NaN)
            if stat == "diff":
                values = values - threedi_result.get_values_by_timestep_nr(parameter, 0)
            # updated to act for actual, display actual value
            elif stat == "act":
                values = values  # removed np.fabs(values) to get actual value

            update_dict = {}
            field_index = layer.fields().lookupField("result")

            for feature in layer.getFeatures():
                ids = int(feature.id())
                # NOTE OF CAUTION: subtracting 1 from id  is mandatory for
                # groundwater because those indexes start from 1 (something to
                # do with a trash element), but for the non-groundwater version
                # it is not. HOWEVER, due to some magic hackery in how the
                # *_result layers are created/copied from the regular result
                # layers, the resulting feature ids also start from 1, which
                # why we need to subtract it in both cases, which btw is
                # purely coincidental.
                # TODO: to avoid all this BS this part should be refactored
                # by passing the index to get_values_by_timestep_nr, which
                # should take this into account
                value = values[ids - 1]
                update_dict[ids] = {field_index: float(value)}

            provider.changeAttributeValues(update_dict)
            # layer.setCacheImage(None)
            layer.triggerRepaint()

    def activate_animator(self):
        pass

    def deactivate_animator(self):
        pass

    def setup_ui(self):
        self.HLayout = QHBoxLayout(self)
        self.setLayout(self.HLayout)
        self.activateButton = QPushButton(self)
        self.activateButton.setCheckable(True)
        self.activateButton.setText("Animation on")
        self.HLayout.addWidget(self.activateButton)

        self.line_parameter_combo_box = QComboBox(self)
        self.node_parameter_combo_box = QComboBox(self)

        self.HLayout.addWidget(self.line_parameter_combo_box)
        self.HLayout.addWidget(self.node_parameter_combo_box)
Ejemplo n.º 5
0
    def _buildfromlist(self, listconfig, multiselect):
        def chunks(l, n):
            """ Yield successive n-sized chunks from l.
            """
            for i in range(0, len(l), n):
                yield l[i:i + n]

        items = listconfig['items']
        wrap = self.config.get('wrap', 0)
        showcolor = self.config.get('always_color', False)
        if wrap > 0:
            rows = list(chunks(items, wrap))
        else:
            rows = [items]

        for rowcount, row in enumerate(rows):
            for column, item in enumerate(row):
                parts = item.split(';')
                data = parts[0]
                try:
                    desc = parts[1]
                except IndexError:
                    desc = data

                button = QPushButton()
                button.setCheckable(multiselect)
                self.group.setExclusive(not multiselect)

                icon = QIcon()
                try:
                    path = parts[2]
                    if path.startswith("#"):
                        # Colour the button with the hex value.
                        # If show color is enabled we always show the color regardless of selection.
                        if not showcolor:
                            style = """
                                QPushButton::checked {{
                                    border: 3px solid rgb(137, 175, 255);
                                    background-color: {colour};
                                }}""".format(colour=path)
                        else:
                            style = """
                                QPushButton::checked {{
                                    border: 3px solid rgb(137, 175, 255);
                                }}
                                QPushButton {{
                                    background-color: {colour};
                                }}""".format(colour=path)
                        button.setStyleSheet(style)
                    elif path.endswith("_icon"):
                        icon = QIcon(":/icons/{}".format(path))
                    else:
                        icon = QIcon(path)
                except:
                    icon = QIcon()

                button.setCheckable(True)
                button.setText(desc)
                button.setProperty("value", data)
                button.setIcon(icon)
                button.setIconSize(QSize(24, 24))
                if isinstance(self.widget.layout(), QBoxLayout):
                    self.widget.layout().addWidget(button)
                else:
                    self.widget.layout().addWidget(button, rowcount, column)
                self.group.addButton(button)
Ejemplo n.º 6
0
class RiverMetricsDockWidget(QDockWidget, FORM_CLASS):

    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        """Constructor."""
        super(RiverMetricsDockWidget, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        # fill the layer combobox with vector layers
        self.vectorCombo.setFilters(QgsMapLayerProxyModel.LineLayer)
        self.validate.clicked.connect(self.validateLayer)
        self.graph.clicked.connect(self.graph_data)

        self.clear_graph.clicked.connect(self.do_clear_graph)
        #self.vectorCombo.currentIndexChanged.connect(self.setup_gui)

        #global variables
        self.validate_test = None
        self.graphicState = False
        self.canvas = None
        self.figure = None
        self.breaks = []  # breaks of river axes
        self.line = None  # line geometry
        self.breakButton = QPushButton('Add breaks')
        self.breakButton.setCheckable(True)
        self.Xcsv = None
        self.Ycsv = None
        self.breakButton.clicked.connect(self.addBreaks)
        self.browseBtn.clicked.connect(self.writeFile)

        self.filecsvtemp = tempfile.NamedTemporaryFile(suffix='.csv')

        self.filecsvpath = os.path.splitext(str(
            self.filecsvtemp.name))[0] + '.csv'
        self.lineOutput.setText(self.filecsvpath)

        #create container plot
        self.setup_gui()

    def closeEvent(self, event):
        self.closingPlugin.emit()
        event.accept()

    def message(self, text, col):
        self.validator.setText(text)
        self.validator.setStyleSheet('background-color: ' + col)

    def setup_gui(self):

        #PLOT container
        # a figure instance to plot on
        self.figure = plt.figure()

        # this is the Canvas Widget that displays the `figure`
        # it takes the `figure` instance as a parameter to __init__
        self.canvas = FigureCanvas(self.figure)

        # this is the Navigation widget
        # it takes the Canvas widget and a parent
        self.toolbar = NavigationToolbar(self.canvas, self)

        #  create a new empty QVboxLayout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.canvas)
        self.layout.addWidget(self.breakButton)

        # set the Qframe layout
        self.frame_for_plot.setLayout(self.layout)  # si alla fine

    def validateLayer(self):
        self.validator.clear()
        self.validator.setStyleSheet('background-color: None')
        vlayer = self.vectorCombo.currentLayer()

        if not vlayer.isValid():
            self.message(vlayer.name() + ' is not valid', 'red')
            self.validate_test = False
        elif vlayer.geometryType() != QgsWkbTypes.LineGeometry:
            self.message('your vector layer is not a Line', 'red')
            self.validate_test = False
        else:
            self.validate_test = True
            self.message(vlayer.name() + ' is valid', 'white')

        if vlayer.featureCount() > 1:
            self.message('You have more then one feature on your vector layer',
                         'yellow')
            self.validate_test = False
        else:
            for feat in vlayer.getFeatures():
                the_geom = feat.geometry()
                len = the_geom.length()
                self.validate_test = True
                if self.stepSpin.value() >= the_geom.length():
                    self.message(
                        'Use a smaller value for step less than ' +
                        str(round(len / 1000, 2)) + ' km', 'yellow')
                    self.validate_test = False

        #TODO -- add validate CRS layer to be not a geographic
        crslayer = vlayer.crs().toProj4()
        if 'proj=longlat' in crslayer:
            self.validate_test = False
            self.message('The layer crs is not projected', 'yellow')
        if crslayer == '':
            self.validate_test = True
            self.message('warning:the crs seems missing', 'yellow')

    def clearLayout(self, layout):
        '''
        clear layout function
        :return:
        '''
        while layout.count():
            child = layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()

    def graph_data(self):

        step = self.stepSpin.value()
        shif = self.shiftSpin.value()
        if self.validate_test == None:
            self.message('You have to validate your layers first', 'yellow')
        if self.validate_test == True:
            vlayer = self.vectorCombo.currentLayer()
            for feat in vlayer.getFeatures():
                the_geom = feat.geometry()
                x, y = sinuosity(the_geom, step, shif)
                self.line = the_geom

            self.figure.clear()
            # create an axis
            ax = self.figure.add_subplot(111)
            ax.plot(x, y, 'bo', x, y, 'k')
            self.Xcsv = x
            self.Ycsv = y

            #def addVline():
            #if breakButton.isChecked():
            #       figure.canvas.mpl_connect('button_press_event', OnClick)

            #TODO: debug
            #self.canvas.show()
            self.canvas.draw()
            #set variable graphState to true to remember graph is plotted
            self.graphicState = True

            self.writecsv()

            #self.graph.hide()

    def addBreaks(self):
        def OnClick(event):
            ax = self.figure.add_subplot(111)
            ax.axvline(event.xdata, linewidth=4, color='r')
            self.canvas.draw()
            self.breaks.append(float(event.xdata))

        if self.graphicState is True:
            if self.breakButton.isChecked():
                self.breakButton.setText('stop-break')
                self.cid = self.figure.canvas.mpl_connect(
                    'button_press_event', OnClick)
            #TODO: create memory layer splitted with breaks
            else:
                self.breakButton.setText('Add Break')
                self.figure.canvas.mpl_disconnect(self.cid)
                self.final()
                #print self.breaks
                # ll1 = createMemLayer(self.line, self.breaks)
                # QgsMapLayerRegistry.instance().addMapLayers([ll1])
        else:
            self.message('You have to graph your data first', 'yellow')

    def final(self):

        vlayer = self.vectorCombo.currentLayer()
        for feat in vlayer.getFeatures():
            the_geom = feat.geometry()
        #TODO: remove the line for debigging
        #self.message(str(vlayer.name())+'|'+str(the_geom.length())+'|'+str(self.breaks), 'red')

        ll1 = createMemLayer(the_geom, self.breaks)
        QgsProject.instance().addMapLayer(ll1)

    def writeFile(self):
        fileName, __ = QFileDialog.getSaveFileName(
            self, 'Save CSV file', "", "CSV (*.csv);;All files (*)")
        fileName = os.path.splitext(str(fileName))[0] + '.csv'
        self.lineOutput.setText(fileName)

    def writecsv(self):
        filecsv = open(self.lineOutput.text(), 'w')
        filecsv.write('length,sinuosity\n')
        for row in range(len(self.Xcsv)):
            filecsv.write(
                str(round(self.Xcsv[row], 4)) + ',' +
                str(round(self.Ycsv[row], 4)) + '\n')
        filecsv.close()

    def do_clear_graph(self):
        self.figure.clear()
        self.canvas.draw()
Ejemplo n.º 7
0
    def initialize_widgets(self):
        """Dynamically set up widgets based on detected files."""
        self.widgets_per_file.clear()
        files_widgets = [
            self.widget_general,
            self.widget_terrain_model,
            self.widget_simple_infiltration,
            self.widget_groundwater,
            self.widget_interflow,
        ]
        files_info_collection = [
            self.general_files,
            self.terrain_model_files,
            self.simple_infiltration_files,
            self.groundwater_files,
            self.interflow_files,
        ]
        for widget in files_widgets:
            widget.hide()

        current_main_layout_row = 1
        for widget, files_info in zip(files_widgets, files_info_collection):
            widget_layout = widget.layout()
            for field_name, name in files_info.items():
                try:
                    file_state = self.detected_files[field_name]
                except KeyError:
                    continue
                status = file_state["status"]
                widget.show()
                name_label = QLabel(name)
                name_label.setSizePolicy(QSizePolicy.Preferred,
                                         QSizePolicy.Preferred)
                widget_layout.addWidget(name_label, current_main_layout_row, 0)

                status_label = QLabel(status.value)
                status_label.setSizePolicy(QSizePolicy.Preferred,
                                           QSizePolicy.Preferred)
                widget_layout.addWidget(status_label, current_main_layout_row,
                                        1)

                empty_label = QLabel()
                widget_layout.addWidget(empty_label, current_main_layout_row,
                                        2)

                no_action_pb_name = "Ignore"
                if status == UploadFileStatus.DELETED_LOCALLY:
                    action_pb_name = "Delete online"
                else:
                    action_pb_name = "Upload"
                # Add valid reference widgets
                all_actions_widget = QWidget()
                actions_sublayout = QGridLayout()
                all_actions_widget.setLayout(actions_sublayout)

                valid_ref_widget = QWidget()
                valid_ref_sublayout = QGridLayout()
                valid_ref_widget.setLayout(valid_ref_sublayout)
                no_action_pb = QPushButton(no_action_pb_name)
                no_action_pb.setCheckable(True)
                no_action_pb.setAutoExclusive(True)
                no_action_pb.clicked.connect(
                    partial(self.toggle_action, field_name, False))

                action_pb = QPushButton(action_pb_name)
                action_pb.setCheckable(True)
                action_pb.setAutoExclusive(True)
                action_pb.setChecked(True)
                action_pb.clicked.connect(
                    partial(self.toggle_action, field_name, True))

                valid_ref_sublayout.addWidget(no_action_pb, 0, 0)
                valid_ref_sublayout.addWidget(action_pb, 0, 1)

                # Add invalid reference widgets
                invalid_ref_widget = QWidget()
                invalid_ref_sublayout = QGridLayout()
                invalid_ref_widget.setLayout(invalid_ref_sublayout)

                filepath_sublayout = QGridLayout()
                filepath_line_edit = QLineEdit()
                filepath_line_edit.setSizePolicy(QSizePolicy.Minimum,
                                                 QSizePolicy.Minimum)
                browse_pb = QPushButton("...")
                browse_pb.clicked.connect(
                    partial(self.browse_for_raster, field_name))
                filepath_sublayout.addWidget(filepath_line_edit, 0, 0)
                filepath_sublayout.addWidget(browse_pb, 0, 1)
                invalid_ref_sublayout.addLayout(filepath_sublayout, 0, 0)

                update_ref_pb = QPushButton("Update reference")
                update_ref_pb.clicked.connect(
                    partial(self.update_raster_reference, field_name))
                invalid_ref_sublayout.addWidget(update_ref_pb, 0, 1)

                actions_sublayout.addWidget(valid_ref_widget, 0, 0)
                actions_sublayout.addWidget(invalid_ref_widget, 0, 1)
                # Add all actions widget into the main widget layout
                widget_layout.addWidget(all_actions_widget,
                                        current_main_layout_row, 2)
                # Hide some of the widgets based on files states
                if status == UploadFileStatus.NO_CHANGES_DETECTED:
                    all_actions_widget.hide()
                elif status == UploadFileStatus.INVALID_REFERENCE:
                    valid_ref_widget.hide()
                else:
                    invalid_ref_widget.hide()
                self.widgets_per_file[field_name] = (
                    name_label,
                    status_label,
                    valid_ref_widget,
                    action_pb,
                    invalid_ref_widget,
                    filepath_line_edit,
                )
                current_main_layout_row += 1