Beispiel #1
0
    def setupUi(self):
        '''
        set up the user interface
        '''
        self.setMinimumWidth(500)
        self.setWindowTitle('Neues Projekt erstellen')

        project_manager = ProjectManager()
        self.project_names = [p.name for p in project_manager.projects]

        layout = QVBoxLayout(self)

        label = QLabel('Name des Projekts')
        self.name_edit = QLineEdit()
        self.name_edit.textChanged.connect(self.validate)
        layout.addWidget(label)
        layout.addWidget(self.name_edit)
        self.path = os.path.join(project_manager.settings.TEMPLATE_PATH,
                                 'projektflaechen')

        hlayout = QHBoxLayout(self)
        label = QLabel('Import der (Teil-)Flächen des Plangebiets')
        self.layer_combo = QgsMapLayerComboBox()
        self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer)

        self.source = None

        self.layer_combo.layerChanged.connect(self.set_layer)
        self.layer_combo.layerChanged.connect(self.validate)
        browse_button = QPushButton('...')
        browse_button.clicked.connect(self.browse_path)
        browse_button.setMaximumWidth(30)
        hlayout.addWidget(self.layer_combo)
        hlayout.addWidget(browse_button)
        layout.addWidget(label)
        layout.addLayout(hlayout)

        self.status_label = QLabel()
        layout.addWidget(self.status_label)

        spacer = QSpacerItem(
            20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        layout.addItem(spacer)

        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        self.ok_button = buttons.button(QDialogButtonBox.Ok)
        self.ok_button.setEnabled(False)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)

        if len(self.layer_combo) > 0:
            self.set_layer(self.layer_combo.currentLayer())
        self.layer_combo.setCurrentIndex(0)
Beispiel #2
0
class TheWidgetItem(QWidget, FORM_CLASS):
    """TheWidgetItem is a class which create item for listWidget."""
    def __init__(self, parent=None):
        """Constructor."""
        super(TheWidgetItem, self).__init__(parent)

        self.setupUi(self)

        self.labels = [
            self.lblPlt1, self.lblPlt2, self.lblPlt3, self.lblPlt4,
            self.lblPlt5, self.lblPlt6
        ]

        self.textQVBoxLayout = QVBoxLayout()
        self.allQHBoxLayout = QHBoxLayout()
        verticalSpacer = QSpacerItem(0, 70, QSizePolicy.Minimum,
                                     QSizePolicy.Expanding)
        self.textQVBoxLayout.addItem(verticalSpacer)
        self.allQHBoxLayout.addLayout(self.textQVBoxLayout)
        self.route_id = -1
        self.setLayout(self.allQHBoxLayout)
        if sys.platform == "linux2":
            for label in self.labels:
                label.setFont(QtGui.QFont('SansSerif', 10))
        if sys.platform == "win32":
            for label in self.labels:
                label.setFont(QtGui.QFont('Arial', 9))

    def set_route_name(self, text, color):
        self.lblRouteName.setText(text)
        self.lblRouteName.setStyleSheet('color: rgb(' + str(color[0]) + ',' +
                                        str(color[1]) + ',' + str(color[2]) +
                                        ')')

    def set_route_id(self, route_id):
        self.route_id = route_id

    def set_distance_time(self, distance, time):
        self.lblDistanceTime.show()
        self.lblDistanceTime.setText("Distance: {}, time: {}".format(
            distance, time))

    def hide_all_lbl_pollutants(self):
        for label in self.labels:
            label.setText("")
            label.hide()

    def set_pollutants(self, idx, plt, value):
        label = self.labels[idx]
        label.setText("{}: {}".format(plt, value))
        label.show()
class ErrorWidgetItem(QWidget, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(ErrorWidgetItem, self).__init__(parent)

        self.setupUi(self)

        self.textQVBoxLayout = QVBoxLayout()
        self.allQHBoxLayout = QHBoxLayout()
        verticalSpacer = QSpacerItem(0, 70, QSizePolicy.Minimum,
                                     QSizePolicy.Expanding)
        self.textQVBoxLayout.addItem(verticalSpacer)
        self.allQHBoxLayout.addLayout(self.textQVBoxLayout)
        self.route_id = -1
        self.setLayout(self.allQHBoxLayout)

    def set_error_msg(self, text):
        self.lblErrorMsg.setText(text)
Beispiel #4
0
    def __init__(self, iface, parent, params):

        QDialog.__init__(self, parent)

        self.iface = iface
        self.parent = parent
        self.params = params

        self.output_reader = None
        self.tool = None
        self.element_ids_nodes = None
        self.element_ids_links = None

        self.nodes_lay = None
        self.links_lay = None

        self.setWindowTitle(Parameters.plug_in_name)

        # Selection changed listeners
        self.params.junctions_vlay.selectionChanged.connect(self.feature_sel_changed)
        self.params.reservoirs_vlay.selectionChanged.connect(self.feature_sel_changed)
        self.params.tanks_vlay.selectionChanged.connect(self.feature_sel_changed)
        self.params.pipes_vlay.selectionChanged.connect(self.feature_sel_changed)
        self.params.pumps_vlay.selectionChanged.connect(self.feature_sel_changed)
        self.params.valves_vlay.selectionChanged.connect(self.feature_sel_changed)

        # self.setMinimumWidth(min_width)
        # self.setMinimumHeight(min_height)
        fra_main_lay = QVBoxLayout(self)

        self.fra_out_file = QFrame(self)
        fra_out_file_lay = QHBoxLayout(self.fra_out_file)
        self.lbl_out_file = QLabel('Simulation output file:')
        self.txt_out_file = QLineEdit('')
        self.txt_out_file.setReadOnly(True)
        self.btn_out_file = QToolButton()
        self.btn_out_file.setText('...')
        self.btn_out_file.clicked.connect(self.btn_out_file_clicked)
        fra_out_file_lay.addWidget(self.lbl_out_file)
        fra_out_file_lay.addWidget(self.txt_out_file)
        fra_out_file_lay.addWidget(self.btn_out_file)

        self.tab_widget = QTabWidget(self)

        # Graphs tab ---------------------------------------------------------------------------------------------------
        self.tab_graphs = QWidget()
        tab_graphs_lay = QHBoxLayout(self.tab_graphs)

        # Left frame
        self.fra_graphs_left = QFrame()
        self.fra_graphs_left.setMaximumWidth(100)
        fra_graphs_left_lay = QVBoxLayout(self.fra_graphs_left)

        self.btn_sel_element = QPushButton('Pick')
        self.btn_sel_element.clicked.connect(self.btn_sel_element_clicked)
        fra_graphs_left_lay.addWidget(self.btn_sel_element)

        # Nodes
        self.grb_nodes = QGroupBox(u'Nodes')
        lay_grb_nodes = QVBoxLayout(self.grb_nodes)

        self.chk_node_demand = QCheckBox('Demand')
        lay_grb_nodes.addWidget(self.chk_node_demand)

        self.chk_node_head =  QCheckBox('Head')
        lay_grb_nodes.addWidget(self.chk_node_head)

        self.chk_node_pressure = QCheckBox('Pressure')
        lay_grb_nodes.addWidget(self.chk_node_pressure)

        self.chk_node_quality = QCheckBox('Quality')
        lay_grb_nodes.addWidget(self.chk_node_quality)

        fra_graphs_left_lay.addWidget(self.grb_nodes)

        # Links
        self.grb_links = QGroupBox(u'Links')
        lay_grb_links = QVBoxLayout(self.grb_links)

        self.chk_link_flow = QCheckBox('Flow')
        lay_grb_links.addWidget(self.chk_link_flow)

        self.chk_link_velocity = QCheckBox('Velocity')
        lay_grb_links.addWidget(self.chk_link_velocity)

        self.chk_link_headloss = QCheckBox('Headloss')
        lay_grb_links.addWidget(self.chk_link_headloss)

        self.chk_link_quality = QCheckBox('Quality')
        lay_grb_links.addWidget(self.chk_link_quality)

        fra_graphs_left_lay.addWidget(self.grb_links)

        self.btn_draw_graph = QPushButton('Draw')
        self.btn_draw_graph.clicked.connect(self.draw_graphs)
        fra_graphs_left_lay.addWidget(self.btn_draw_graph)

        self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        fra_graphs_left_lay.addItem(self.spacer)

        tab_graphs_lay.addWidget(self.fra_graphs_left)

        # Right frame
        self.fra_graphs_right = QFrame()
        fra_graphs_right_lay = QVBoxLayout(self.fra_graphs_right)
        fra_graphs_right_lay.setContentsMargins(0, 0, 0, 0)

        self.static_canvas = StaticMplCanvas(self.fra_graphs_right, width=5, height=4, dpi=100)
        fra_graphs_right_lay.addWidget(self.static_canvas)

        tab_graphs_lay.addWidget(self.fra_graphs_right)

        # lay.addWidget(self.button)
        self.tab_widget.addTab(self.tab_graphs, 'Graphs')

        # Maps tab -----------------------------------------------------------------------------------------------------
        self.tab_maps = QWidget()
        tab_maps_lay = QHBoxLayout(self.tab_maps)

        # Left frame
        self.fra_maps_left = QFrame()
        self.fra_maps_left.setMaximumWidth(200)
        fra_maps_left_lay = QVBoxLayout(self.fra_maps_left)

        self.grb_maps = QGroupBox(u'Variable')
        grb_maps_lay = QVBoxLayout(self.grb_maps)

        self.rad_maps_node_demand = QRadioButton(u'Node demand')
        grb_maps_lay.addWidget(self.rad_maps_node_demand)

        self.rad_maps_node_head = QRadioButton(u'Node head')
        grb_maps_lay.addWidget(self.rad_maps_node_head)

        self.rad_maps_node_pressure = QRadioButton(u'Node pressure')
        grb_maps_lay.addWidget(self.rad_maps_node_pressure)

        self.rad_maps_node_quality = QRadioButton(u'Node quality')
        grb_maps_lay.addWidget(self.rad_maps_node_quality)

        self.rad_maps_link_flow = QRadioButton(u'Link flow')
        grb_maps_lay.addWidget(self.rad_maps_link_flow)

        self.rad_maps_link_velocity = QRadioButton(u'Link velocity')
        grb_maps_lay.addWidget(self.rad_maps_link_velocity)

        self.rad_maps_link_headloss = QRadioButton(u'Link headloss')
        grb_maps_lay.addWidget(self.rad_maps_link_headloss)

        self.rad_maps_link_quality = QRadioButton(u'Link quality')
        grb_maps_lay.addWidget(self.rad_maps_link_quality)

        fra_maps_left_lay.addWidget(self.grb_maps)
        fra_maps_left_lay.addItem(self.spacer)

        tab_maps_lay.addWidget(self.fra_maps_left)

        # Right maps frame
        self.fra_maps_right = QFrame()
        fra_maps_right_lay = QVBoxLayout(self.fra_maps_right)

        self.fra_maps_right_time = QFrame()
        fra_maps_right_time_lay = QFormLayout(self.fra_maps_right_time)

        self.lbl_map_times = QLabel(u'Period [h]:')
        self.cbo_map_times = QComboBox()
        fra_maps_right_time_lay.addRow(self.lbl_map_times, self.cbo_map_times)
        fra_maps_right_lay.addWidget(self.fra_maps_right_time)

        self.btn_draw_map = QPushButton(u'Draw map')
        self.btn_draw_map.clicked.connect(self.draw_maps)
        fra_maps_right_lay.addWidget(self.btn_draw_map)

        fra_maps_right_lay.addItem(self.spacer)

        tab_maps_lay.addWidget(self.fra_maps_right)

        self.tab_widget.addTab(self.tab_maps, 'Maps')

        # # Add to main
        fra_main_lay.addWidget(self.fra_out_file)
        fra_main_lay.addWidget(self.tab_widget)

        self.setup()
        self.initialize()
        # self.read_outputs()

        # Set size
        self.setMinimumWidth(self.tab_graphs.width())
        self.setMinimumHeight(self.tab_graphs.height())
Beispiel #5
0
def on_resolve_href(dialog, layer, feature, field):
    """
    @param dialog the dialog where the feature form is opened
    @param layer the layer on which the href link stands
    @param feature the current feature
    @param field the field name storing the href URL
    @param linked_layer_id the QGIS layer id of the already resolved layer, for update
    """
    from .import_gmlas_panel import ImportGmlasPanel
    path = feature[field]
    if not path:
        return

    # if parent is a Dialog, we are in a feature form
    # else in a attribute table
    is_feature_form = isinstance(dialog.parent, QDialog)

    # The href is resolved thanks to the OGR GMLAS driver.
    # We need to determine what is the "root" layer of the imported
    # href, so that we can connect the xlink:href link to the
    # newly loaded set of layers.
    # There seems to be no way to determine what is the "root" layer
    # of a GMLAS database.
    # So, we rely on XML parsing to determine the root element
    # and on layer xpath found in metadata

    # Download the file so that it is used for XML parsing
    # and for GMLAS loading
    from ..core.qgis_urlopener import remote_open_from_qgis
    from ..core.gml_utils import extract_features
    from ..core.xml_utils import xml_root_tag, no_ns, no_prefix
    import tempfile

    with remote_open_from_qgis(path) as fi:
        with tempfile.NamedTemporaryFile(delete=False) as fo:
            fo.write(fi.read())
            tmp_file = fo.name

    with open(tmp_file, 'r') as file_io:
        root_tag = xml_root_tag(file_io)

    # reuse the GMLAS import panel widget
    dlg = QDialog()
    import_widget = ImportGmlasPanel(dlg, gml_path=tmp_file)
    path_edit = QLineEdit(path, dlg)
    path_edit.setEnabled(False)
    btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dlg)
    layout = QVBoxLayout()
    layout.addWidget(path_edit)
    layout.addWidget(import_widget)
    layout.addItem(
        QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
    layout.addWidget(btn)
    dlg.setLayout(layout)
    btn.accepted.connect(dlg.accept)
    btn.rejected.connect(dlg.reject)
    dlg.resize(400, 300)
    dlg.setWindowTitle("Options for xlink:href loading")
    if not dlg.exec_():
        return

    # close the current form
    w = dialog
    while not isinstance(w, QDialog):
        w = w.parent()
    w.close()

    import_widget.do_load()
    # Add a link between the current layer
    # and the root layer of the newly loaded (complex) features

    # 1. determine the root layer and pkid of all its features
    root_layer = None
    for l in QgsProject.instance().mapLayers().values():
        if no_ns(l.customProperty("xpath", "")) == no_prefix(root_tag):
            root_layer = l
            break
    if root_layer is None:
        raise RuntimeError("Cannot determine the root layer")

    pkid = layer.customProperty("pkid")
    pkid_value = feature[pkid]
    root_layer.startEditing()
    # 2. add a href_origin_pkid field in the root layer
    if "parent_href_pkid" not in [f.name() for f in root_layer.fields()]:
        new_field = QgsField(layer.fields().field(pkid))
        new_field.setName("parent_href_pkid")
        root_layer.addAttribute(new_field)

    # 3. set its value to the id of current feature
    ids_to_change = []
    for f in root_layer.getFeatures():
        if f["parent_href_pkid"] is None:
            ids_to_change.append(f.id())
    idx = root_layer.fields().indexFromName("parent_href_pkid")
    for fid in ids_to_change:
        # sets the pkid_value
        root_layer.changeAttributeValue(fid, idx, pkid_value)

    root_layer.commitChanges()

    # 4. declare a new QgsRelation
    rel_name = "1_n_" + layer.name() + "_" + field
    rel = QgsProject.instance().relationManager().relations().get(rel_name)
    if rel is None:
        rel = QgsRelation()
        rel.setId(rel_name)
        rel.setName(field)
        rel.setReferencedLayer(layer.id())
        rel.setReferencingLayer(root_layer.id())
        rel.addFieldPair("parent_href_pkid", pkid)
        QgsProject.instance().relationManager().addRelation(rel)

    # 5. declare the new relation in the form widgets
    # new 1:N in the current layer
    fc = layer.editFormConfig()
    rel_tab = fc.tabs()[1]
    rel_tab.addChildElement(
        QgsAttributeEditorRelation(rel.name(), rel, rel_tab))
    # new field in the root layer
    fc = root_layer.editFormConfig()
    main_tab = fc.tabs()[0]
    main_tab.addChildElement(
        QgsAttributeEditorField("parent_href_pkid", idx, main_tab))
    # declare as reference relation widget
    s = QgsEditorWidgetSetup(
        "RelationReference", {
            'AllowNULL': False,
            'ReadOnly': True,
            'Relation': rel.id(),
            'OrderByValue': False,
            'MapIdentification': False,
            'AllowAddFeatures': False,
            'ShowForm': True
        })
    root_layer.setEditorWidgetSetup(idx, s)

    # write metadata in layers
    href_resolved = layer.customProperty("href_resolved", [])
    if path not in href_resolved:
        layer.setCustomProperty("href_resolved", href_resolved + [path])
    href_linked_layers = layer.customProperty("href_linked_layers", {})
    href_linked_layers[field] = root_layer.id()
    layer.setCustomProperty("href_linked_layers", href_linked_layers)

    # 6. reload the current form
    from ..main import get_iface
    if is_feature_form:
        get_iface().openFeatureForm(layer, feature)
    else:
        get_iface().showAttributeTable(layer)
    def show(self, *args, title: str = 'Parameter einstellen',
             scrollable: bool = False):
        '''
        render parameters and elements in parent

        Parameters
        ----------
        args : optional
            arguments for appending parameter layout to parent
            (like x, y if parent is grid layout)
        title : str, optional
            title of the parameter dialog, defaults to 'Parameter einstellen'
        scrollable : bool, optional
            a scrollbar will be added to both preview and dialog if True,
            recommended if there are a lot of parameters, defaults to not
            scrollable
        '''
        if self.parent is None:
            raise Exception("can't render Params object with no parent set")

        # Debug: function to automatically write a help file with all params
        # with empty texts, should be removed in production
        if (settings.DEBUG and getattr(self, 'help_file', None) and
            not os.path.exists(self.help_file)):
            if not os.path.exists(self.HELP_PATH):
                os.mkdir(self.HELP_PATH)
            with open(self.help_file, 'w') as json_file:
                json.dump(self.help_dict, json_file, indent=4)

        self.dialog = ParamsDialog(parent=None,
                                   help_text=self.help_dict['beschreibung'],
                                   title=title)

        self.parent.addLayout(self.layout, *args)

        if scrollable:
            frame = QFrame()
            scroll_area = QScrollArea()
            layout = QVBoxLayout()
            layout.setSpacing(5)
            frame.setLayout(layout)
            scroll_area.setWidget(frame)
            scroll_area.setWidgetResizable(True)
            scroll_area.setFixedHeight(400)
            self.layout.addWidget(scroll_area)
        else:
            layout = self.layout

        for element in self._elements:
            if isinstance(element, QLayoutItem):
                layout.addItem(element)
            # overview
            elif not getattr(element, 'hide_in_overview', None):
                element.draw(layout)
            self.dialog.draw(element)

        if not self.editable:
            return

        row = QHBoxLayout()
        button = QPushButton(self.button_label)
        icon = QIcon(os.path.join(settings.IMAGE_PATH, 'iconset_mob',
                                  '20190619_iconset_mob_edit_1.png'))
        button.setIcon(icon)
        tool_tip = self.help_dict.get('tooltip', None)
        button.setToolTip(tool_tip)
        row.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
        row.addWidget(button)
        self.layout.addItem(
            QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum))
        self.layout.addLayout(row)

        button.clicked.connect(self.show_dialog)
class Params(QObject):
    '''
    collection of parameters, single parameters can be added by setting
    them as attributes. supports dict-like access of the parameters by name

    Attributes
    ----------
    changed : pyqtSignal
        fired on change of any parameter in collection
    '''
    changed = pyqtSignal()
    HELP_PATH = os.path.join(settings.HELP_PATH, 'params')

    def __init__(self, parent: QObject = None,
                 button_label: str = 'Editieren', editable: bool = True,
                 help_text: str = '', help_file: str = None):
        '''
        Parameters
        ----------
        parent : QObject, optional
            ui element to draw the parameters in, can't be drawn if parent is
            None, defaults to no parent
        button_label : parent, optional
            label of the edit button in the parameter preview, defaults to
            'Editieren'
        editable : bool, optional
            parameters are editable. if not, no edit button will be shown in the
            preview, defaults to editable parameters
        help_file : str, optional
            json-style text file containing help texts for each parameter,
            will be automatically created if not existing, defaults to no help
            file
        help_text : str, optional
            the general help displayed in the parameter dialog, overrides the
            description in the help file if given, defaults no help text
        '''
        super().__init__()
        self._params = OrderedDict()
        self._elements = []
        self.button_label = button_label
        #self._dependencies = []
        self.parent = parent
        self.dialog = None
        self.editable = editable
        self.layout = QVBoxLayout()
        self.layout.setSpacing(5)
        self.help_dict = {
            'tooltip': 'Parameter editieren'
        }
        if help_file:
            self.help_file = help_file if os.path.exists(help_file) else \
                os.path.join(self.HELP_PATH, help_file)
            if os.path.exists(self.help_file):
                with open(self.help_file) as json_file:
                    self.help_dict = yaml.safe_load(json_file)
        # passed help text overrides the one from file
        if help_text or 'beschreibung' not in self.help_dict:
            self.help_dict['beschreibung'] = help_text

    def add(self, element: Union[Param, Seperator, Title, QLayoutItem],
            name: str = ''):
        '''
        add an element (parameter or style element)
        elements will be rendered in order of addition

        Parameters
        ----------
        element : object
            parameter or style element to add
        name : str, optional
            name of parameter to add, parameter can be adressed by that name.
            ignored when element is not a parameter, defaults to no name

        '''
        self._elements.append(element)
        if name and isinstance(element, Param):
            self._params[name] = element
            if element.input:
                if element.help_text or name not in self.help_dict:
                    self.help_dict[name] = element.help_text
                else:
                    element.help_text = self.help_dict[name]

    @property
    def params(self) -> List[Param]:
        '''
        Returns
        -------
        list
            a list of all parameters
        '''
        return self._params.values()

    def show(self, *args, title: str = 'Parameter einstellen',
             scrollable: bool = False):
        '''
        render parameters and elements in parent

        Parameters
        ----------
        args : optional
            arguments for appending parameter layout to parent
            (like x, y if parent is grid layout)
        title : str, optional
            title of the parameter dialog, defaults to 'Parameter einstellen'
        scrollable : bool, optional
            a scrollbar will be added to both preview and dialog if True,
            recommended if there are a lot of parameters, defaults to not
            scrollable
        '''
        if self.parent is None:
            raise Exception("can't render Params object with no parent set")

        # Debug: function to automatically write a help file with all params
        # with empty texts, should be removed in production
        if (settings.DEBUG and getattr(self, 'help_file', None) and
            not os.path.exists(self.help_file)):
            if not os.path.exists(self.HELP_PATH):
                os.mkdir(self.HELP_PATH)
            with open(self.help_file, 'w') as json_file:
                json.dump(self.help_dict, json_file, indent=4)

        self.dialog = ParamsDialog(parent=None,
                                   help_text=self.help_dict['beschreibung'],
                                   title=title)

        self.parent.addLayout(self.layout, *args)

        if scrollable:
            frame = QFrame()
            scroll_area = QScrollArea()
            layout = QVBoxLayout()
            layout.setSpacing(5)
            frame.setLayout(layout)
            scroll_area.setWidget(frame)
            scroll_area.setWidgetResizable(True)
            scroll_area.setFixedHeight(400)
            self.layout.addWidget(scroll_area)
        else:
            layout = self.layout

        for element in self._elements:
            if isinstance(element, QLayoutItem):
                layout.addItem(element)
            # overview
            elif not getattr(element, 'hide_in_overview', None):
                element.draw(layout)
            self.dialog.draw(element)

        if not self.editable:
            return

        row = QHBoxLayout()
        button = QPushButton(self.button_label)
        icon = QIcon(os.path.join(settings.IMAGE_PATH, 'iconset_mob',
                                  '20190619_iconset_mob_edit_1.png'))
        button.setIcon(icon)
        tool_tip = self.help_dict.get('tooltip', None)
        button.setToolTip(tool_tip)
        row.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
        row.addWidget(button)
        self.layout.addItem(
            QSpacerItem(10, 10, QSizePolicy.Fixed, QSizePolicy.Minimum))
        self.layout.addLayout(row)

        button.clicked.connect(self.show_dialog)

    def get(self, name: str) -> Param:
        '''
        get a parameter by name

        Returns
        -------
        Param
            the parameter matching the name or None if not found
        '''
        return self._params.get(name, None)

    def close(self):
        '''
        close rendered parameters
        '''
        if self.dialog:
            clear_layout(self.dialog.layout)
            del(self.dialog)
        clear_layout(self.layout)

    def show_dialog(self):
        '''
        show the dialog to edit parameters
        '''
        confirmed = self.dialog.exec_()
        if confirmed:
            has_changed = False
            for param in self.params:
                if not param.input or param.value == param.input.value:
                    continue
                param.value = param.input.value
                has_changed = True
            if has_changed:
                self.changed.emit()
        else:
            # reset inputs
            for param in self.params:
                if param.input:
                    param.input.value = param.value

    def __getattr__(self, name):
        param = self._params.get(name, None)
        if param:
            return param
        return self.__dict__.get(name, None)

    def __setattr__(self, name, value):
        if isinstance(value, Param):
            self.add(value, name)
        else:
            self.__dict__[name] = value

    def __getitem__(self, key):
        return self._params.get(key, None)

    def __setitem__(self, key, value):
        self.add(value, key)
class DisplayAequilibraEFormatsDialog(QtWidgets.QDialog, FORM_CLASS):
    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()

    def format_showing(self):
        decimals = self.decimals.value()
        separator = self.thousand_separator.isChecked()
        if isinstance(self.data_to_show, AequilibraeMatrix):
            m = NumpyModel(self.data_to_show, separator, decimals)
        else:
            m = DatabaseModel(self.data_to_show, separator, decimals)
        self.table.clearSpans()
        self.table.setModel(m)

    def change_matrix_cores(self):
        self.data_to_show.computational_view([self.mat_list.currentText()])
        self.data_to_show.set_index(self.data_to_show.index_names[0])
        self.format_showing()

    def export(self):
        new_name, file_type = GetOutputFileName(
            self, self.data_type, ["Comma-separated file(*.csv)"], ".csv",
            self.data_path)
        if new_name is not None:
            self.data_to_show.export(new_name)

    def exit_with_error(self):
        qgis.utils.iface.messageBar().pushMessage("Error:",
                                                  self.error,
                                                  level=1)
        self.exit_procedure()

    def exit_procedure(self):
        self.close()
Beispiel #9
0
class ProgressDialog(QDialog):
    """ Progress dialog shows progress bar for algorithm.
    """

    def __init__(self, iface):
        QDialog.__init__(self, iface.mainWindow())
        self.workerThread = None
        self.state = False
        self.resultStatus = None
        self.doReRun = False
        self.wasCanceled = False
        self.wasSuccessful = False
        self.savedProj = None
        self.result = None
        self.messageTxt = {
            'msg_optimierung': self.tr('Berechnung der optimalen Stuetzenpositionen...'),
            'msg_seillinie': self.tr('Berechnung der optimale Seillinie...')
        }
        
        # Build GUI Elements
        self.setWindowTitle(self.tr("SEILAPLAN wird ausgefuehrt"))
        self.resize(500, 100)
        self.container = QVBoxLayout()
        self.progressBar = QProgressBar(self)
        self.progressBar.setMinimumWidth(500)
        self.statusLabel = QLabel(self)
        self.hbox = QHBoxLayout()
        self.cancelButton = QDialogButtonBox()
        self.closeButton = QDialogButtonBox()
        self.resultLabel = QLabel(self)
        self.resultLabel.setMaximumWidth(500)
        self.resultLabel.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding))
        self.resultLabel.setWordWrap(True)
        spacer1 = QSpacerItem(20, 20, QSizePolicy.Fixed,
                              QSizePolicy.Fixed)
        self.rerunButton = QPushButton(self.tr("zurueck zum Startfenster"))
        self.rerunButton.setVisible(False)
        spacer2 = QSpacerItem(40, 20, QSizePolicy.Expanding,
                             QSizePolicy.Minimum)
        self.cancelButton.setStandardButtons(QDialogButtonBox.Cancel)
        self.cancelButton.button(QDialogButtonBox.Cancel).setText(self.tr("Abbrechen"))
        self.cancelButton.clicked.connect(self.onAbort)
        self.closeButton.setStandardButtons(QDialogButtonBox.Close)
        self.closeButton.button(QDialogButtonBox.Close).setText(self.tr("Schliessen"))
        self.closeButton.clicked.connect(self.onClose)
        self.hbox.addWidget(self.rerunButton)
        self.hbox.addItem(spacer2)
        self.hbox.addWidget(self.cancelButton)
        self.hbox.setAlignment(self.cancelButton, Qt.AlignHCenter)
        self.hbox.addWidget(self.closeButton)
        self.hbox.setAlignment(self.closeButton, Qt.AlignHCenter)
        self.closeButton.hide()
        
        self.container.addWidget(self.progressBar)
        self.container.addWidget(self.statusLabel)
        self.container.addWidget(self.resultLabel)
        self.container.addItem(spacer1)
        self.container.addLayout(self.hbox)
        self.container.setSizeConstraint(QLayout.SetFixedSize)
        self.setLayout(self.container)

    # noinspection PyMethodMayBeStatic
    def tr(self, message, **kwargs):
        """Get the translation for a string using Qt translation API.
        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString

        Parameters
        ----------
        **kwargs
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate(type(self).__name__, message)
        
    def setThread(self, workerThread):
        self.workerThread = workerThread
        self.connectThreadSignals()
    
    def connectThreadSignals(self):
        # Connect signals of thread
        self.workerThread.sig_jobEnded.connect(self.jobEnded)
        self.workerThread.sig_jobError.connect(self.onError)
        self.workerThread.sig_value.connect(self.valueFromThread)
        self.workerThread.sig_range.connect(self.rangeFromThread)
        self.workerThread.sig_text.connect(self.textFromThread)
        self.workerThread.sig_result.connect(self.resultFromThread)
        self.rerunButton.clicked.connect(self.onRerun)
        
    def run(self):
        # Show modal dialog window (QGIS is still responsive)
        self.show()
        # start event loop
        self.exec()
    
    def jobEnded(self, success):
        self.setWindowTitle("SEILAPLAN")
        if success:
            self.progressBar.setValue(self.progressBar.maximum())
            self.wasSuccessful = True
            # Close progress dialog so that adjustment window can be opened
            self.close()
        else:  # If there was an abort by the user
            self.statusLabel.setText(self.tr("Berechnungen abgebrochen."))
            self.progressBar.setValue(self.progressBar.minimum())
            self.finallyDo()
    
    def valueFromThread(self, value):
        self.progressBar.setValue(int(value))
    
    def rangeFromThread(self, range_vals):
        self.progressBar.setRange(int(round(range_vals[0])), int(round(range_vals[1])))
    
    def maxFromThread(self, max):
        self.progressBar.setValue(self.progressBar.maximum())
    
    def textFromThread(self, message):
        self.statusLabel.setText(self.messageTxt[message])
    
    def resultFromThread(self, resultStatus):
        self.resultStatus = resultStatus
        # resultStatus:
        #   1 = Optimization successful
        #   2 = Cable takes off from support
        #   3 = Optimization partially successful
    
    def onAbort(self):
        self.setWindowTitle('SEILAPLAN')
        self.statusLabel.setText(self.tr(
            'Laufender Prozess wird abgebrochen...'))
        self.workerThread.cancel()  # Terminates process cleanly
        self.wasCanceled = True
    
    def onError(self, exception_string):
        self.setWindowTitle(self.tr('SEILAPLAN: Berechnung fehlgeschlagen'))
        self.statusLabel.setText(self.tr('Ein Fehler ist aufgetreten:'))
        self.resultLabel.setText(self.tr(exception_string))
        self.resultLabel.setHidden(False)
        self.progressBar.setValue(self.progressBar.minimum())
        self.setLayout(self.container)
        self.finallyDo()
    
    def onRerun(self):
        self.doReRun = True
        self.onClose()
    
    def finallyDo(self):
        self.rerunButton.setVisible(True)
        self.cancelButton.hide()
        self.closeButton.show()
    
    def onClose(self):
        self.close()
Beispiel #10
0
class ProfileDialog(QDialog):
    def __init__(self, parent, interface, drawTool, projectHandler):
        """
        :type drawTool: gui.mapMarker.MapMarkerTool
        :type projectHandler: projectHandler.ProjectConfHandler
        """
        QDialog.__init__(self, parent)
        self.iface = interface
        self.projectHandler = projectHandler
        self.drawTool = drawTool
        self.setWindowTitle(self.tr('Gelaendelinie'))
        self.setWindowModality(Qt.WindowModal)
        
        self.profile = None
        # Array with properties fixed poles
        self.poleData = []
        # Array with sections without poles
        self.noPoleSection = []
        # Profile data
        self.xdata = None
        self.zdata = None
        self.profileMin = 0
        self.profileMax = None

        # Plot
        self.sc = ProfilePlot(self)
        # Pan/Zoom Tools for diagram
        tbar = MyNavigationToolbar(self.sc, self)
        tbar.pan()
        self.sc.setToolbar(tbar)

        # Layout
        main_widget = QWidget(self)
        self.container = QVBoxLayout(main_widget)
        self.outerLayout = QVBoxLayout()

        # GUI fields
        stueTitle = QLabel('<b>' + self.tr('Stuetzenoptimierung einschraenken') + '</b>')
        hbox = QHBoxLayout()
        line1 = QFrame()
        line1.setFrameShape(QFrame.HLine)
        line1.setFrameShadow(QFrame.Sunken)

        # Create labels and buttons
        self.fixStueAdd = QPushButton(self.tr('Fixe Stuetze definieren'))
        self.noStueAdd = QPushButton(self.tr('Abschnitt ohne Stuetzen definieren'))
        self.noStueDel = QPushButton()
        icon = QIcon()
        icon.addPixmap(
            QPixmap(':/plugins/SeilaplanPlugin/gui/icons/icon_bin.png'),
            QIcon.Normal, QIcon.Off)
        self.noStueDel.setIcon(icon)
        self.noStueDel.setIconSize(QSize(16, 16))
        self.fixStueAdd.setToolTip(self.tr('Tooltip Fixe Stuetzen'))
        self.noStueAdd.setToolTip(self.tr('Tooltip Abschnitte ohne Stuetzen'))
        self.noStueDel.setToolTip(self.tr('Tooltip Abschnitte loeschen'))
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                        QSizePolicy.Minimum)
        hbox.addWidget(self.fixStueAdd)
        hbox.addItem(spacerItem1)
        hbox.addWidget(self.noStueAdd)
        hbox.addWidget(self.noStueDel)
        hbox.setAlignment(self.noStueAdd, Qt.AlignRight)
        btnBoxSpacer = QSpacerItem(40, 40, QSizePolicy.Fixed,
                                        QSizePolicy.Fixed)
        self.buttonBox = QDialogButtonBox(main_widget)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok)
        # Build up Gui
        self.container.addWidget(self.sc)
        self.container.addWidget(tbar, alignment=Qt.AlignHCenter | Qt.AlignTop)
        self.container.addWidget(line1)
        self.container.addWidget(stueTitle)
        self.container.addLayout(hbox)
        self.container.addLayout(self.outerLayout)
        self.container.addItem(btnBoxSpacer)
        self.container.addWidget(self.buttonBox)

        # Connect signals
        self.fixStueAdd.clicked.connect(self.sc.acitvateCrosshairPole)
        self.noStueAdd.clicked.connect(self.sc.activateCrosshairSection)
        self.noStueDel.clicked.connect(self.deleteSections)
        self.buttonBox.accepted.connect(self.Apply)
        self.setLayout(self.container)
        
        # Gui's functionality for fixed pole gui fields
        self.buildPoleHeader()
        self.poleLayout = CustomPoleWidget(self, self.outerLayout, self.poleData)
        self.poleLayout.sig_updatePole.connect(self.updatePole)
        self.poleLayout.sig_deletePole.connect(self.deletePole)

    # noinspection PyMethodMayBeStatic
    def tr(self, message, **kwargs):
        """Get the translation for a string using Qt translation API.
        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString

        Parameters
        ----------
        **kwargs
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate(type(self).__name__, message)
    
    def setProfile(self, profile):
        self.profile = profile
        self.xdata = self.profile.di
        self.zdata = self.profile.zi
        self.profileMax = floor(self.xdata[-1])
        # Draw profile in diagram
        self.sc.plotData(self.profile)
    
    def reset(self):
        """Resets the window and remove all pole layouts. Markers do not have
        to be deleted, they are reset when new profile line is drawn. Plot
        points are reset when plot is cleared at next setProfile()."""
        # Delete pole rows in gui
        self.poleLayout.removeAll()
        self.poleData = []
        self.noPoleSection = []
        self.profileMax = None
        self.sc.reset()

    def setPoleData(self, poles, sections):
        """Fills gui, plot and map with data of fixed poles and pole
        sections."""
        self.drawTool.removeIntermediateMarkers()
        # Make sure the pole layout has the correct reference to the pole data
        self.poleLayout.poleArr = self.poleData
        # Set the max ranges
        self.poleLayout.setInitialGui([self.profileMin, self.profileMax])
        for pole in poles:
            self.addPole(pole['d'], pole['z'], pole['h'], name=pole['name'])
        for section in sections:
            # Draw line onto map
            self.activateMapLine(section[0])
            self.finishLine(section[1])
            # Plot section in plot
            for x in section:
                z = self.getZValue(x)
                self.sc.drawSection(x, z)
        self.sc.draw()

    def buildPoleHeader(self):
        headerRow = QHBoxLayout()
        spacerItemA = QSpacerItem(60, 20, QSizePolicy.Fixed,
                                 QSizePolicy.Minimum)
        spacerItemE = QSpacerItem(60, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        headername = QLabel(self.tr('Stuetzenbezeichnung'))
        headername.setMinimumSize(QSize(180, 30))
        headerDist = QLabel(self.tr('Hori.distanz'))
        headerDist.setMinimumSize(QSize(95, 30))
        headerHeight = QLabel(self.tr('Hoehe'))
        headerHeight.setMinimumSize(QSize(85, 30))

        headerRow.addItem(spacerItemA)
        headerRow.addWidget(headername)
        headerRow.addWidget(headerDist)
        headerRow.addWidget(headerHeight)
        headerRow.addItem(spacerItemE)
        self.outerLayout.addLayout(headerRow)
    
    def addPole(self, d, z, h=None, name='', angle=False):
        """Called when user clicks onto plot window to create fixed pole.
        Function creates a new row in gui with properties of pole, creates a
        point in the plot and a marker on the map."""
        idx = 0
        for i, pole in enumerate(reversed(self.poleData)):
            if pole['d'] <= d:
                idx = len(self.poleData) - i
                break
        if not z:
            z = self.getZValue(d)
        if not name:
            name = self.tr('fixe Stuetze')
        # Draw point on plot
        drawnPoint = self.sc.createPoint(d, z)
        # Draw marker onto map
        self.createMapMarker(d, idx+1)
        # Save new fixed pole in list
        self.poleData.insert(idx, {
            'name': name,
            'nr': idx+1,
            'poleType': 'fixed',
            'd': d,
            'z': z,
            'h': h,
            'angle': angle,
            'plotPoint': drawnPoint
        })
        self.poleLayout.addRow(idx, addBtn=False)
        
    def updatePole(self, idx, property_name, val):
        """Called when user manually changes distance or height values in
        LineEdits. Function updates point in plot and marker on map."""
        if self.poleData[idx][property_name] == val:
            return
        if property_name == 'd':
            # Calculate z value
            self.poleData[idx]['z'] = self.getZValue(val)
            # Update in plot
            self.sc.deletePoint(self.poleData[idx]['plotPoint'])
            self.poleData[idx]['plotPoint'] = \
                self.sc.createPoint(val, self.poleData[idx]['z'])
            # Update on map
            marker = self.projectHandler.transform2MapCoords(float(val))
            self.drawTool.updateMarker(marker, idx+1)
        
        if property_name in ['d', 'h']:
            self.poleData[idx][property_name] = round(val, 0)
        else:
            self.poleData[idx][property_name] = val
        self.poleLayout.changeRow(idx, property_name, val)
    
    def deletePole(self, idx):
        """Called when user clicks on delete button on pole row. Function
        removes point in plot, marker on map and row in gui."""
        self.sc.deletePoint(self.poleData[idx]['plotPoint'])
        self.drawTool.removeMarker(idx+1)
        self.poleLayout.deleteRow(idx)
        self.poleData.pop(idx)
    
    def getZValue(self, dist):
        return self.zdata[np.argmax(self.xdata >= dist)]
    
    def createMapMarker(self, horiDist, idx):
        point = self.projectHandler.transform2MapCoords(float(horiDist))
        self.drawTool.drawMarker(point, idx)
        
    def deactivateMapCursor(self):
        self.drawTool.deactivateCursor()

    def activateMapCursor(self, initPoint, color):
        self.updateMapCursor(initPoint, color)

    def updateMapCursor(self, horiDist, color):
        point = self.projectHandler.transform2MapCoords(horiDist)
        self.drawTool.updateCursor(point, color)

    def activateMapLine(self, horiDist):
        self.noPoleSection.append([horiDist, None])
        initPoint = self.projectHandler.transform2MapCoords(horiDist)
        self.drawTool.activateSectionLine(initPoint)

    def lineMoved(self, horiDist):
        point = self.projectHandler.transform2MapCoords(horiDist)
        self.drawTool.updateSectionLine(point)

    def finishLine(self, horiDist):
        self.noPoleSection[-1][1] = horiDist
        endPoint = self.projectHandler.transform2MapCoords(horiDist)
        self.drawTool.updateSectionLine(endPoint)
        self.drawTool.deactivateCursor()
    
    def deleteSections(self):
        if self.noPoleSection:
            # Redraw profile
            self.sc.plotData(self.profile)
            # Redraw markers of pole
            for pole in self.poleData:
                pole['plotPoint'] = self.sc.createPoint(pole['d'], pole['z'])
            # Delete all sections in map
            self.drawTool.deleteSectionLines()
            self.noPoleSection = []
    
    def stopActiveEdits(self):
        self.deactivateMapCursor()
        self.drawTool.clearUnfinishedLines()
        # Remove last section if its not finished
        if self.noPoleSection and not self.noPoleSection[-1][1]:
            self.noPoleSection.pop(-1)

    def Apply(self):
        self.close()
    
    def closeEvent(self, event):
        self.stopActiveEdits()
        self.projectHandler.setFixedPoles(self.poleData)
        self.projectHandler.setNoPoleSection(self.noPoleSection)
        # Reset gui since this can only be done when the window is still open
        self.reset()
class GraphDockWidget(QDockWidget):
    """Main Dock Widget for showing 3Di results in Graphs"""

    closingWidget = pyqtSignal(int)

    def __init__(
        self,
        iface,
        parent_widget=None,
        parent_class=None,
        nr=0,
        ts_datasources=None,
        root_tool=None,
    ):
        """Constructor"""
        super().__init__(parent_widget)

        self.iface = iface
        self.parent_class = parent_class
        self.nr = nr
        self.ts_datasources = ts_datasources
        self.root_tool = root_tool

        self.setup_ui(self)

        parameter_config = self._get_active_parameter_config()

        # add graph widgets
        self.q_graph_widget = GraphWidget(
            self,
            self.ts_datasources,
            parameter_config["q"],
            "Q graph",
            QgsWkbTypes.LineString,
        )
        self.h_graph_widget = GraphWidget(
            self,
            self.ts_datasources,
            parameter_config["h"],
            "H graph",
            QgsWkbTypes.Point,
        )
        self.graphTabWidget.addTab(self.q_graph_widget,
                                   self.q_graph_widget.name)
        self.graphTabWidget.addTab(self.h_graph_widget,
                                   self.h_graph_widget.name)

        # add listeners
        self.addSelectedObjectButton.clicked.connect(self.add_objects)
        # init current layer state and add listener
        self.selected_layer_changed(self.iface.mapCanvas().currentLayer)
        self.iface.currentLayerChanged.connect(self.selected_layer_changed)
        self.root_tool.timeslider_widget.datasource_changed.connect(
            self.on_active_ts_datasource_change)

    def on_close(self):
        """
        unloading widget and remove all required stuff
        :return:
        """
        self.addSelectedObjectButton.clicked.disconnect(self.add_objects)
        self.iface.currentLayerChanged.disconnect(self.selected_layer_changed)
        self.root_tool.timeslider_widget.datasource_changed.disconnect(
            self.on_active_ts_datasource_change)

        # self.q_graph_widget.close()
        # self.h_graph_widget.close()

    def closeEvent(self, event):
        """
        overwrite of QDockWidget class to emit signal
        :param event: QEvent
        """
        self.on_close()
        self.closingWidget.emit(self.nr)
        event.accept()

    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
            available_agg_vars = threedi_result.available_aggregation_vars
            if not available_agg_vars:
                messagebar_message("Warning",
                                   "No aggregation netCDF was found.",
                                   level=1,
                                   duration=5)
            parameter_config = generate_parameter_config(
                available_subgrid_vars, available_agg_vars)
        else:
            parameter_config = {"q": {}, "h": {}}

        return parameter_config

    def on_active_ts_datasource_change(self):

        parameter_config = self._get_active_parameter_config()
        self.q_graph_widget.set_parameter_list(parameter_config["q"])
        self.h_graph_widget.set_parameter_list(parameter_config["h"])

    def selected_layer_changed(self, active_layer):

        tdi_layer = False

        # get active layer from canvas, otherwise .dataProvider doesn't work
        canvas = self.iface.mapCanvas()
        current_layer = canvas.currentLayer()

        if current_layer:
            provider = current_layer.dataProvider()
            valid_object_type = normalized_object_type(current_layer.name())

            if provider.name() in VALID_PROVIDERS and valid_object_type:
                tdi_layer = True
            elif current_layer.name() in ("flowlines", "nodes"):
                tdi_layer = True

        # activate button if 3Di layers found
        self.addSelectedObjectButton.setEnabled(tdi_layer)

    def add_objects(self):
        canvas = self.iface.mapCanvas()
        current_layer = canvas.currentLayer()
        if not current_layer:
            # todo: feedback select layer first
            return

        provider = current_layer.dataProvider()
        if provider.name() not in VALID_PROVIDERS:
            return

        if current_layer.name() not in list(LAYER_QH_TYPE_MAPPING.keys()):
            if current_layer.name() not in ("flowlines", "nodes"):
                # todo: feedback layer not supported
                return

        selected_features = current_layer.selectedFeatures()

        if current_layer.name() == "flowlines":
            self.q_graph_widget.add_objects(current_layer, selected_features)
            self.graphTabWidget.setCurrentIndex(
                self.graphTabWidget.indexOf(self.q_graph_widget))
            return
        elif current_layer.name() == "nodes":
            self.h_graph_widget.add_objects(current_layer, selected_features)
            self.graphTabWidget.setCurrentIndex(
                self.graphTabWidget.indexOf(self.h_graph_widget))
            return

        if LAYER_QH_TYPE_MAPPING[current_layer.name()] == "q":
            self.q_graph_widget.add_objects(current_layer, selected_features)
            self.graphTabWidget.setCurrentIndex(
                self.graphTabWidget.indexOf(self.q_graph_widget))
        else:
            self.h_graph_widget.add_objects(current_layer, selected_features)
            self.graphTabWidget.setCurrentIndex(
                self.graphTabWidget.indexOf(self.h_graph_widget))

    def on_btnstate(self, state):
        """Toggle ``absolute`` state of the GraphPlots"""
        checked = state == Qt.Checked
        self.q_graph_widget.graph_plot.absolute = (
            self.h_graph_widget.graph_plot.absolute) = checked

    def setup_ui(self, dock_widget):
        """
        initiate main Qt building blocks of interface
        :param dock_widget: QDockWidget instance
        """

        dock_widget.setObjectName("dock_widget")
        dock_widget.setAttribute(Qt.WA_DeleteOnClose)

        self.dockWidgetContent = QWidget(self)
        self.dockWidgetContent.setObjectName("dockWidgetContent")

        self.mainVLayout = QVBoxLayout(self.dockWidgetContent)
        self.dockWidgetContent.setLayout(self.mainVLayout)

        # add button to add objects to graphs
        self.buttonBarHLayout = QHBoxLayout(self)
        self.addSelectedObjectButton = QPushButton(self.dockWidgetContent)
        self.addSelectedObjectButton.setObjectName("addSelectedObjectButton")
        self.checkbox = QCheckBox("Absolute", parent=self.dockWidgetContent)
        self.checkbox.setChecked(False)
        self.checkbox.stateChanged.connect(self.on_btnstate)
        self.buttonBarHLayout.addWidget(self.addSelectedObjectButton)
        self.buttonBarHLayout.addWidget(self.checkbox)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.buttonBarHLayout.addItem(spacerItem)
        self.mainVLayout.addItem(self.buttonBarHLayout)

        # add tabWidget for graphWidgets
        self.graphTabWidget = QTabWidget(self.dockWidgetContent)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(6)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.graphTabWidget.sizePolicy().hasHeightForWidth())
        self.graphTabWidget.setSizePolicy(sizePolicy)
        self.graphTabWidget.setObjectName("graphTabWidget")
        self.mainVLayout.addWidget(self.graphTabWidget)

        # add dockwidget
        dock_widget.setWidget(self.dockWidgetContent)
        self.retranslate_ui(dock_widget)
        QMetaObject.connectSlotsByName(dock_widget)

    def retranslate_ui(self, DockWidget):
        DockWidget.setWindowTitle("3Di result plots %i" % self.nr)
        self.addSelectedObjectButton.setText("Add")