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
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)
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)
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()
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