From d096944af6778b5bc77783bf63d838cc44d75270 Mon Sep 17 00:00:00 2001 From: soaubier <117665766+soaubier@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:14:03 +0200 Subject: [PATCH 01/11] Add radar plot type (#374) * first radar plot add * Clean up: remove print(), improve variable names ... * Set max threshold value & adjust QDoubleSpinBox size * add radar icons * Load configuration settings added * Add font style management * Adjust widget size for threshold * manage CLI radar option * fixup! Load configuration settings added * minor refacto for a better y_radar_values management * correct default settings * fix bad variable usage * Update DataPlotly/core/plot_types/radar.py Co-authored-by: Jacky Volpes <34267385+Djedouas@users.noreply.github.com> * Update DataPlotly/gui/plot_settings_widget.py * review * add traduction to radar.py * radar svg update * fix default settings for line_type_threshold * re-add feature_subset_defined option for radar plot * fix missing import * fix radar threshold line type and combo box are now decorrelated * remove duplicated line * fix widgets synchronization Refresh the UI according to Points, Line, Points and Lines marker type not only when marker type changes, but also when the plot type changes (to be correctly initialized). * add default values for radar threshold --------- Co-authored-by: Jacky Volpes Co-authored-by: Jacky Volpes <34267385+Djedouas@users.noreply.github.com> --- DataPlotly/core/plot_factory.py | 36 +- DataPlotly/core/plot_settings.py | 11 +- DataPlotly/core/plot_types/__init__.py | 1 + DataPlotly/core/plot_types/icons/radar.svg | 1770 +++++++++++++++++++ DataPlotly/core/plot_types/radar.py | 109 ++ DataPlotly/gui/plot_settings_widget.py | 112 +- DataPlotly/ui/dataplotly_dockwidget_base.ui | 633 ++++--- 7 files changed, 2347 insertions(+), 325 deletions(-) create mode 100644 DataPlotly/core/plot_types/icons/radar.svg create mode 100644 DataPlotly/core/plot_types/radar.py diff --git a/DataPlotly/core/plot_factory.py b/DataPlotly/core/plot_factory.py index 759d2d34..604277ad 100644 --- a/DataPlotly/core/plot_factory.py +++ b/DataPlotly/core/plot_factory.py @@ -163,6 +163,14 @@ def add_source_field_or_expression(field_or_expression): z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \ self.settings.properties[ 'z_name'] else (None, False, set()) + y_label_expression, _, y_label_attrs = add_source_field_or_expression(self.settings.properties['y_combo_radar_label']) if \ + self.settings.properties[ + 'y_combo_radar_label'] else (None, False, set()) + y_fields_expression = QgsExpression("array(" + ", ".join([f'"{field_name}"' + for field_name in self.settings.properties['y_fields_combo'].split(", ") + ]) + ")") if \ + self.settings.properties[ + 'y_fields_combo'] else None additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression( self.settings.layout['additional_info_expression']) if self.settings.layout[ 'additional_info_expression'] else (None, False, set()) @@ -171,6 +179,7 @@ def add_source_field_or_expression(field_or_expression): x_attrs, y_attrs, z_attrs, + y_label_attrs, additional_attrs) request = QgsFeatureRequest() @@ -230,6 +239,9 @@ def add_source_field_or_expression(field_or_expression): colors = [] stroke_colors = [] stroke_widths = [] + y_radar_labels = [] + y_radar_values = [] + for f in it: if visible_geom_engine and not visible_geom_engine.intersects(f.geometry().constGet()): continue @@ -267,6 +279,22 @@ def add_source_field_or_expression(field_or_expression): if z == NULL or z is None: continue + y_radar_label = None + if y_label_expression: + y_radar_label = y_label_expression.evaluate(context) + if y_radar_label == NULL or y_radar_label is None: + continue + elif self.settings.properties['y_combo_radar_label']: + y_radar_label = f[self.settings.properties['y_combo_radar_label']] + if y_radar_label == NULL or y_radar_label is None: + continue + + y_radar_value = None + if y_fields_expression: + y_radar_value = y_fields_expression.evaluate(context) + if y_radar_value == NULL or y_radar_value is None: + continue + if additional_info_expression: additional_hover_text.append( additional_info_expression.evaluate(context)) @@ -280,6 +308,10 @@ def add_source_field_or_expression(field_or_expression): yy.append(y) if z is not None: zz.append(z) + if y_radar_label is not None: + y_radar_labels.append(y_radar_label) + if y_radar_value is not None: + y_radar_values.append(y_radar_value) if self.settings.data_defined_properties.isActive(PlotSettings.PROPERTY_MARKER_SIZE): default_value = self.settings.properties['marker_size'] @@ -338,6 +370,8 @@ def add_source_field_or_expression(field_or_expression): self.settings.x = xx self.settings.y = yy self.settings.z = zz + self.settings.y_radar_labels = y_radar_labels + self.settings.y_radar_values = y_radar_values if marker_sizes: self.settings.data_defined_marker_sizes = marker_sizes if colors: @@ -699,7 +733,6 @@ def build_figure(self) -> str: with open(self.plot_path, "w", encoding="utf8") as f: f.write(self.build_html(config)) - return self.plot_path def build_figures(self, plot_type, ptrace, config=None) -> str: @@ -740,7 +773,6 @@ def build_figures(self, plot_type, ptrace, config=None) -> str: self.layout = PlotFactory.PLOT_TYPES[plot_type].create_layout( self.settings) figures = go.Figure(data=ptrace, layout=self.layout) - else: figures = go.Figure(data=ptrace, layout=self.layout) diff --git a/DataPlotly/core/plot_settings.py b/DataPlotly/core/plot_settings.py index 420ccee7..b78e643d 100644 --- a/DataPlotly/core/plot_settings.py +++ b/DataPlotly/core/plot_settings.py @@ -101,6 +101,8 @@ def __init__(self, plot_type: str = 'scatter', properties: dict = None, layout: 'x_name': '', 'y_name': '', 'z_name': '', + 'y_combo_radar_label': '', + 'y_fields_combo': '', 'in_color': '#8ebad9', 'out_color': '#1f77b4', 'marker_width': 1, @@ -135,7 +137,12 @@ def __init__(self, plot_type: str = 'scatter', properties: dict = None, layout: 'show_mean_line': False, 'layout_filter_by_map': False, 'layout_filter_by_atlas': False, - 'pie_hole': 0 + 'pie_hole': 0, + 'fill': False, + 'line_combo_threshold': 'Dot Line', + 'line_dash_threshold': 'dash', + 'threshold_value': 1, + 'threshold': False } # layout nested dictionary @@ -205,6 +212,8 @@ def __init__(self, plot_type: str = 'scatter', properties: dict = None, layout: self.x = [] self.y = [] self.z = [] + self.y_radar_labels = [] + self.y_radar_values = [] self.feature_ids = [] self.additional_hover_text = [] self.data_defined_marker_sizes = [] diff --git a/DataPlotly/core/plot_types/__init__.py b/DataPlotly/core/plot_types/__init__.py index 3f033e43..418cc9be 100644 --- a/DataPlotly/core/plot_types/__init__.py +++ b/DataPlotly/core/plot_types/__init__.py @@ -14,6 +14,7 @@ from .histogram import HistogramFactory from .pie import PieChartFactory from .polar import PolarChartFactory +from .radar import RadarChartFactory from .scatter import ScatterPlotFactory from .ternary import TernaryFactory from .violin import ViolinFactory diff --git a/DataPlotly/core/plot_types/icons/radar.svg b/DataPlotly/core/plot_types/icons/radar.svg new file mode 100644 index 00000000..6261c28a --- /dev/null +++ b/DataPlotly/core/plot_types/icons/radar.svg @@ -0,0 +1,1770 @@ + + + +image/svg+xml diff --git a/DataPlotly/core/plot_types/radar.py b/DataPlotly/core/plot_types/radar.py new file mode 100644 index 00000000..299257ad --- /dev/null +++ b/DataPlotly/core/plot_types/radar.py @@ -0,0 +1,109 @@ +""" +Radar chart factory + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" + +import os +from plotly import graph_objs +from qgis.PyQt.QtCore import QCoreApplication +from qgis.PyQt.QtGui import QIcon +from DataPlotly.core.plot_types.plot_type import PlotType +import plotly.colors as pc + +import numpy as np +class RadarChartFactory(PlotType): + """ + Factory for radar charts + """ + + @staticmethod + def type_name(): + return 'radar' + + @staticmethod + def name(): + return PlotType.tr('Radar Plot') + + @staticmethod + def icon(): + return QIcon(os.path.join(os.path.dirname(__file__), 'icons/radar.svg')) + + @staticmethod + def create_trace(settings): + + if len(settings.y_radar_values) == 0: + return [] + + x = settings.properties["y_fields_combo"].split(", ") + + # Sample colors from the color scale based on the length of settings.y_radar_values + colors_list = pc.sample_colorscale(settings.properties['color_scale'], np.linspace(0, 1, len(settings.y_radar_values[0]))) + # List repeating the line type for each element in settings.y_radar_values + line_type_list = [settings.properties['line_dash']] * len(settings.y_radar_values) + + # Add a black color and a threshold line to the data + if settings.properties['threshold']: + colors_list.append('#000000') + settings.y_radar_values.append([settings.properties['threshold_value']] * len(settings.y_radar_values[0])) + settings.y_radar_labels.append(QCoreApplication.translate('DataPlotly', 'threshold')) + line_type_list.append(settings.properties['line_dash_threshold']) + + radar_plot_list = [] + for (y, name, colors_list, line_type_list) in zip(settings.y_radar_values, settings.y_radar_labels, colors_list, line_type_list): + # If the marker type includes lines, close the plot by repeating the first (x, y) point + if settings.properties['marker'] in ('lines', 'lines+markers'): + x.append(x[0]) + y.append(y[0]) + + radar_plot_list.append(graph_objs.Scatterpolar( + r=y, + theta=x, + mode=settings.properties['marker'], + name=name, + marker={ + "color": colors_list, + "size": settings.data_defined_marker_sizes if settings.data_defined_marker_sizes else settings.properties['marker_size'], + "symbol": settings.properties['marker_symbol'], + "line": { + "color": settings.properties['out_color'], + "width": settings.properties['marker_width'] + } + }, + line={ + "color": colors_list, + "width": settings.data_defined_stroke_widths if settings.data_defined_stroke_widths else settings.properties['marker_width'], + "dash": line_type_list + + }, + opacity=settings.properties['opacity'], + fill="toself" if settings.properties['fill'] else None + )) + + return radar_plot_list + + @staticmethod + def create_layout(settings): + layout = super(RadarChartFactory, RadarChartFactory).create_layout(settings) + + layout['polar'] = settings.layout['polar'] + layout['polar'].update({ + 'radialaxis': { + 'tickfont':{ + "size": settings.layout.get('font_xticks_size',30), + "color": settings.layout.get('font_xticks_color',"#00000"), + "family": settings.layout.get('font_xticks_family', "Arial"), + } + }, + 'angularaxis':{ + 'tickfont':{ + "size": settings.layout.get('font_ylabel_size',30), + "color": settings.layout.get('font_ylabel_color',"#00000"), + "family": settings.layout.get('font_ylabel_family', "Arial") + } + } + }) + return layout diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index 9ec9ec20..5d57f1f7 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -44,7 +44,8 @@ from qgis.PyQt.QtCore import ( QUrl, pyqtSignal, - QDir + QDir, + Qt ) from qgis.PyQt.QtWebKit import QWebSettings from qgis.PyQt.QtWebKitWidgets import ( @@ -79,7 +80,6 @@ WIDGET, _ = uic.loadUiType( GuiUtils.get_ui_file_path('dataplotly_dockwidget_base.ui')) - class DataPlotlyPanelWidget(QgsPanelWidget, WIDGET): # pylint: disable=too-many-lines,too-many-instance-attributes,too-many-public-methods """ Main configuration panel widget for plot settings @@ -182,7 +182,6 @@ def __init__(self, mode=MODE_CANVAS, parent=None, override_iface=None, message_b for clazz in type_classes: self.plot_combo.addItem( clazz.icon(), clazz.name(), clazz.type_name()) - # default to scatter plots self.set_plot_type('scatter') @@ -194,7 +193,6 @@ def __init__(self, mode=MODE_CANVAS, parent=None, override_iface=None, message_b # widgets self.refreshWidgets() self.refreshWidgets2() - self.refreshWidgets3() self.plot_combo.currentIndexChanged.connect(self.refreshWidgets) self.plot_combo.currentIndexChanged.connect(self.helpPage) self.subcombo.currentIndexChanged.connect(self.refreshWidgets2) @@ -493,11 +491,15 @@ def selected_layer_changed(self, layer): """ Trigger actions after selected layer changes """ + self.y_fields_combo.clear() self.x_combo.setLayer(layer) self.y_combo.setLayer(layer) + self.y_combo_radar_label.setLayer(layer) self.z_combo.setLayer(layer) self.additional_info_combo.setLayer(layer) + if layer is not None : + self.y_fields_combo.addItems([field.name() for field in layer.fields()]) buttons = self.findChildren(QgsPropertyOverrideButton) for button in buttons: button.setVectorLayer(layer) @@ -638,7 +640,6 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch # get the plot type from the combobox self.ptype = self.plot_combo.currentData() - # BoxPlot BarPlot and Histogram orientation (same values) self.orientation_combo.clear() self.orientation_combo.addItem(self.tr('Vertical'), 'v') @@ -724,8 +725,12 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch ]) self.line_combo.clear() + self.line_combo_threshold.clear() for k, v in self.line_types.items(): self.line_combo.addItem(k, v) + self.line_combo_threshold.addItem(k,v) + + # BarPlot bar mode self.bar_mode_combo.clear() @@ -764,10 +769,11 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch 'BlackRedYellowBlue': 'Blackbody', 'Terrain': 'Earth', 'Electric Scale': 'Electric', - 'RedOrangeYellow': 'YIOrRd', - 'DeepblueBlueWhite': 'YIGnBu', + 'RedOrangeYellow': 'YlOrRd', # fix from https://github.com/plotly/graphing-library-docs/issues/14 + 'DeepblueBlueWhite': 'YlGnBu', # fix from https://github.com/plotly/graphing-library-docs/issues/14 'BlueWhitePurple': 'Picnic'} + self.color_scale_combo.clear() self.color_scale_data_defined_in.clear() @@ -790,7 +796,7 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.register_data_defined_button( self.in_color_defined_button, PlotSettings.PROPERTY_COLOR) - elif self.ptype in ('scatter', 'ternary', 'bar', '2dhistogram', 'contour', 'polar'): + elif self.ptype in ('scatter', 'ternary', 'bar', '2dhistogram', 'contour', 'polar','radar'): self.x_label.setText(self.tr('X field')) self.x_label.setFont(self.font()) @@ -868,8 +874,12 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch # plot properties self.layer_combo: ['all'], self.feature_subset_defined_button: ['all'], - self.x_label: ['all'], - self.x_combo: ['all'], + self.x_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar', 'ternary', 'violin', 'contour'], + self.x_combo: ['scatter', 'bar', 'box','pie' '2dhistogram','histogram', 'polar', 'ternary', 'violin', 'contour'], + self.y_fields_label: ['radar'], + self.y_fields_combo: ['radar'], + self.y_combo_radar_label: ['radar'], + self.y_radar_label: ['radar'], self.y_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram', 'polar', 'ternary', 'contour', 'violin'], self.y_combo: ['scatter', 'bar', 'box', 'pie', '2dhistogram', 'polar', 'ternary', 'contour', 'violin'], self.z_label: ['ternary'], @@ -889,14 +899,14 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.marker_width_lab: ['scatter', 'bar', 'box', 'histogram', 'polar', 'ternary', 'violin'], self.marker_width: ['scatter', 'bar', 'box', 'histogram', 'polar', 'ternary', 'violin'], self.stroke_defined_button: ['scatter', 'bar', 'box', 'histogram', 'polar', 'ternary', 'violin'], - self.marker_size_lab: ['scatter', 'polar', 'ternary', 'bar'], - self.marker_size: ['scatter', 'polar', 'ternary', 'bar'], - self.size_defined_button: ['scatter', 'polar', 'ternary', 'bar'], - self.marker_type_lab: ['scatter', 'polar'], - self.marker_type_combo: ['scatter', 'polar'], - self.alpha_lab: ['scatter', 'bar', 'box', 'histogram', 'polar', 'ternary', 'violin', 'contour'], - self.opacity_widget: ['scatter', 'bar', 'box', 'pie', 'histogram', 'polar', 'ternary', 'violin', 'contour'], - self.properties_group_box: ['scatter', 'bar', 'box', 'pie', 'histogram', 'polar', 'ternary', 'contour', '2dhistogram', + self.marker_size_lab: ['scatter', 'polar', 'ternary', 'bar', 'radar'], + self.marker_size: ['scatter', 'polar', 'ternary', 'bar', 'radar'], + self.size_defined_button: ['scatter', 'polar','ternary', 'bar'], + self.marker_type_lab: ['scatter', 'polar','radar'], + self.marker_type_combo: ['scatter', 'polar','radar'], + self.alpha_lab: ['scatter', 'bar', 'box', 'histogram', 'polar','radar', 'ternary', 'violin', 'contour'], + self.opacity_widget: ['scatter', 'bar', 'box', 'pie', 'histogram', 'polar', 'radar','ternary', 'violin', 'contour'], + self.properties_group_box: ['scatter', 'bar', 'box', 'pie', 'histogram', 'polar', 'radar','ternary', 'contour', '2dhistogram', 'violin'], self.bar_mode_lab: ['bar', 'histogram'], self.bar_mode_combo: ['bar', 'histogram'], @@ -905,14 +915,13 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.legend_title_defined_button: ['all'], self.point_lab: ['scatter', 'ternary', 'polar'], self.point_combo: ['scatter', 'ternary', 'polar'], - self.line_lab: ['scatter', 'polar'], - self.line_combo: ['scatter', 'polar'], - self.color_scale_label: ['contour', '2dhistogram'], - self.color_scale_combo: ['contour', '2dhistogram'], + self.line_lab: ['scatter', 'polar', 'radar',], + self.line_combo: ['scatter', 'polar', 'radar'], + self.color_scale_label: ['contour', '2dhistogram', 'radar'], + self.color_scale_combo: ['contour', '2dhistogram', 'radar'], self.contour_type_label: ['contour'], self.contour_type_combo: ['contour'], self.show_lines_check: ['contour'], - # layout customization self.show_legend_check: ['all'], self.orientation_legend_check: ['scatter', 'bar', 'box', 'histogram', 'ternary', 'pie', 'violin'], @@ -920,20 +929,20 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.plot_title_line: ['all'], self.plot_title_defined_button: ['all'], self.font_title_label: ['all'], - self.font_xlabel_label: ['all'], + self.font_xlabel_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_xticks_label: ['all'], self.font_ylabel_label: ['all'], - self.font_yticks_label: ['all'], + self.font_yticks_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_title_style: ['all'], - self.font_xlabel_style: ['all'], + self.font_xlabel_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_xticks_style: ['all'], self.font_ylabel_style: ['all'], - self.font_yticks_style: ['all'], + self.font_yticks_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_title_color: ['all'], - self.font_xlabel_color: ['all'], + self.font_xlabel_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_xticks_color: ['all'], self.font_ylabel_color: ['all'], - self.font_yticks_color: ['all'], + self.font_yticks_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.x_axis_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], self.x_axis_title: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], self.x_axis_title_defined_button: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], @@ -989,8 +998,14 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.violinBox: ['violin'], self.pie_hole_label : ['pie'], self.pie_hole : ['pie'], - } + self.fill : ['radar'], + self.threshold: ['radar'], + self.threshold_value: ['radar'], + self.line_threshold_value: ['radar'], + self.line_combo_threshold: ['radar'], + self.threshold_value_label: ['radar'] + } # enable the widget according to the plot type for k, v in self.widgetType.items(): if 'all' in v or self.ptype in v: @@ -1010,6 +1025,8 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.color_scale_data_defined_in_check.setVisible(False) self.color_scale_data_defined_in_invert_check.setVisible(False) + self.refreshWidgets3() + def refreshWidgets2(self): """ just refresh the UI to make the radiobuttons visible when SubPlots @@ -1069,6 +1086,7 @@ def setLegend(self): self.legend_title.setText(self.y_combo.currentText()) elif self.ptype == 'histogram': self.legend_title.setText(self.x_combo.currentText()) + else: legend_title_string = ( f'{self.x_combo.currentText()} - {self.y_combo.currentText()}') @@ -1080,7 +1098,6 @@ def get_settings(self) -> PlotSettings: # pylint: disable=R0915 """ # get the plot type from the combo box self.ptype = self.plot_combo.currentData() - # if colorscale should be visible or not color_scale_visible = self.color_scale_data_defined_in_check.isVisible( ) and self.color_scale_data_defined_in_check.isChecked() @@ -1128,7 +1145,14 @@ def get_settings(self) -> PlotSettings: # pylint: disable=R0915 'show_lines_check': self.show_lines_check.isChecked(), 'layout_filter_by_map': self.filter_by_map_check.isChecked(), 'layout_filter_by_atlas': self.filter_by_atlas_check.isChecked(), - 'pie_hole' : self.pie_hole.value() + 'pie_hole': self.pie_hole.value(), + 'fill': self.fill.isChecked(), + 'threshold': self.threshold.isChecked(), + 'y_combo_radar_label': self.y_combo_radar_label.currentText(), + 'line_dash_threshold': self.line_types2[self.line_combo_threshold.currentText()], + 'line_combo_threshold': self.line_combo_threshold.currentText(), + 'threshold_value': self.threshold_value.value(), + 'y_fields_combo': ', '.join(self.y_fields_combo.checkedItems()) } if self.in_color_defined_button.isActive(): @@ -1146,6 +1170,7 @@ def get_settings(self) -> PlotSettings: # pylint: disable=R0915 # build the layout customizations layout_properties = {'legend': self.show_legend_check.isChecked(), 'legend_orientation': 'h' if self.orientation_legend_check.isChecked() else 'v', + 'title': self.plot_title_line.text(), 'font_title_size': max( self.font_title_style.currentFont().pixelSize(), @@ -1190,7 +1215,6 @@ def get_settings(self) -> PlotSettings: # pylint: disable=R0915 'additional_info_expression': self.additional_info_combo.expression(), 'bins_check': self.bins_check.isChecked(), 'gridcolor': self.layout_grid_axis_color.color().name()} - settings = PlotSettings(plot_type=self.ptype, properties=plot_properties, layout=layout_properties, source_layer_id=self.layer_combo.currentLayer().id( ) if self.layer_combo.currentLayer() else None, @@ -1357,13 +1381,21 @@ def set_settings(self, settings: PlotSettings): # pylint: disable=too-many-stat self.layout_grid_axis_color.setColor( QColor(settings.layout.get('gridcolor') or '#bdbfc0')) self.pie_hole.setValue(settings.properties.get('pie_hole', 0)) + for name in settings.properties.get('y_fields_combo', '').split(", "): + self.y_fields_combo.setItemCheckState(self.y_fields_combo.findText(name), Qt.CheckState.Checked) + self.line_combo_threshold.setCurrentText( + settings.properties.get('line_combo_threshold', 'Dash Line') + ) + self.y_combo_radar_label.setExpression(settings.properties.get('y_combo_radar_label', '')) + self.threshold.setChecked(settings.properties.get('threshold', True)) + self.threshold_value.setValue(settings.properties.get('threshold_value', 1)) + self.fill.setChecked(settings.properties.get('fill', False)) def create_plot_factory(self) -> PlotFactory: """ Creates a PlotFactory based on the settings defined in the dialog """ settings = self.get_settings() - visible_region = None if settings.properties['visible_features_only']: visible_region = QgsReferencedRectangle(self.iface.mapCanvas().extent(), @@ -1371,7 +1403,6 @@ def create_plot_factory(self) -> PlotFactory: # plot instance plot_factory = PlotFactory(settings, visible_region=visible_region) - # unique name for each plot trace (name is idx_plot, e.g. 1_scatter) self.pid = f'{self.idx}_{settings.plot_type}' @@ -1424,17 +1455,15 @@ def create_plot(self): if self.subcombo.currentData() == 'single': # plot single plot, check the object dictionary length - if len(self.plot_factories) <= 1: + if len(self.plot_factories) <= 1 or self.ptype == 'radar': self.plot_path = plot_factory.build_figure() # to plot many plots in the same figure else: # plot list ready to be called within go.Figure pl = [] - for _, v in self.plot_factories.items(): pl.append(v.trace[0]) - self.plot_path = plot_factory.build_figures(self.ptype, pl) # choice to draw subplots instead depending on the combobox @@ -1606,7 +1635,6 @@ def showPlotFromDic(self, plot_input_dic): # plot type in the plot_combo combobox self.plot_combo.setCurrentIndex( self.plot_combo.findData(plot_input_dic["plot_type"])) - try: self.layer_combo.setLayer(plot_input_dic["layer"]) if 'x_name' in plot_input_dic["plot_prop"] and plot_input_dic["plot_prop"]["x_name"]: @@ -1615,13 +1643,17 @@ def showPlotFromDic(self, plot_input_dic): self.y_combo.setField(plot_input_dic["plot_prop"]["y_name"]) if 'z_name' in plot_input_dic["plot_prop"] and plot_input_dic["plot_prop"]["z_name"]: self.z_combo.setField(plot_input_dic["plot_prop"]["z_name"]) + if 'y_radar_label' in plot_input_dic["plot_prop"] and plot_input_dic["plot_prop"]["y_radar_label"]: + self.y_combo_radar_label.setField(plot_input_dic["plot_prop"]["y_radar_label"]) + if 'y_radar_fields' in plot_input_dic["plot_prop"] and plot_input_dic["plot_prop"]["y_radar_fields"]: + for name in plot_input_dic["plot_prop"]["y_radar_fields"]: + self.y_fields_combo.setItemCheckState(self.y_fields_combo.findText(name), Qt.CheckState.Checked) except: # pylint: disable=bare-except # noqa: F401 pass settings = PlotSettings(plot_input_dic['plot_type'], properties=plot_input_dic["plot_prop"], layout=plot_input_dic["layout_prop"]) - # create Plot instance factory = PlotFactory(settings) diff --git a/DataPlotly/ui/dataplotly_dockwidget_base.ui b/DataPlotly/ui/dataplotly_dockwidget_base.ui index 2cfd3f03..7b763325 100644 --- a/DataPlotly/ui/dataplotly_dockwidget_base.ui +++ b/DataPlotly/ui/dataplotly_dockwidget_base.ui @@ -320,9 +320,9 @@ QListWidget::item::selected { 0 - -270 + 0 673 - 989 + 1200 @@ -357,15 +357,56 @@ QListWidget::item::selected { Plot Parameters - + + + + + + + ... + + + + + + + Linked map + + + + Y field - - + + + + true + + + Use only selected features + + + + + + + Use only features visible in map + + + + + + + true + + + Use only visible features + + @@ -392,37 +433,43 @@ QListWidget::item::selected { - - + + - Use only features inside atlas feature + Y fields - + + + + Z field - - + + + + + + + + - Use only features visible in map + Feature subset - - + + - ... + Use only features inside atlas feature - - - @@ -433,39 +480,9 @@ QListWidget::item::selected { - - - - Linked map - - - - - - - true - - - Use only selected features - - - - + - - - - - - - true - - - Use only visible features - - - @@ -473,13 +490,16 @@ QListWidget::item::selected { - - + + - Feature subset + Y label + + + @@ -489,378 +509,422 @@ QListWidget::item::selected { Properties - - - - - - + + + ... - - - - - 0 - 0 - + + + + Legend title - - - - If checked, box plots will be overlaid on top of violin plots - + + - Include box plots + Show statistics + + + + + + + + + + + + Visible true - - + + + + Invert color + + + + + + + + + + - Violin side + Marker color - - + + - ... + Stroke color - - + + - Marker size + ... - - + + + + + + + + - Line type + Hover tooltip - - + + + + Contour type + + - - + + + + + + + - Marker color + Show lines + + + true - - - - Color scale - - + - + + + + - + ... - - - - - + + - - + + - Point type + Label text position - - + + - Show mean line + Threshold true - - - - - - - + + + + + - Visible + Marker size - - true + + + + + + + + + + 0 + 0 + - - + + - Invert color + Bar orientation - + + + + Violin side + + - - + + + + Opacity + + + + + + + + - Outliers + Threshold line type - + Qt::StrongFocus - - - - Manual bin size - - + + - - + + - Legend title + Line type - - + + - Color scale + Normalization - - + + - - + + + + + - Contour type + Manual bin size - - + + - - + + - Hover tooltip + Pie hole - - - - - - - - - Show lines + + + + false - - true + + 1000 + + + 10 - + + + + + 0 + 0 + + + - - - - - - true - + + - Invert histogram direction + Fill - - - - true - + + - Cumulative histogram + Outliers - - - - + + - ... + Stroke width - - - - + ... - - - - Opacity + + + + 0.950000000000000 + + + 0.050000000000000 + + + 0.000000000000000 + + + false - - - - - - - - 0 - 0 - + + + + Color scale - - - - - - - false - - - 1000 - - - 10 + + + + ... - - - - - - - - + + - - + + - Bar orientation + Hover label as text - - + + + + Point type + + - - + + + + If checked, box plots will be overlaid on top of violin plots + - Normalization + Include box plots + + + true - - + + + + + + true + - Stroke width + Invert histogram direction - - + + + + true + - Show statistics + Cumulative histogram - - + - - - - Label text position - - + + - + Marker type - - - - - + + - Stroke color + Show mean line - - - - - - Hover label as text + + true - - + + - Pie hole + Color scale - - + + + + + - 0.950000000000000 - - - 0.050000000000000 + 99990000.000000000000000 - 0.000000000000000 + 1.000000000000000 - - false + + + + + + + + + Threshold value @@ -906,8 +970,8 @@ QListWidget::item::selected { 0 0 - 414 - 583 + 449 + 820 @@ -1499,6 +1563,11 @@ QListWidget::item::selected { + + QgsCheckableComboBox + QComboBox +
qgscheckablecombobox.h
+
QgsCollapsibleGroupBox QGroupBox @@ -1558,22 +1627,6 @@ QListWidget::item::selected {
- - x_combo - fieldChanged(QString) - x_axis_title - setText(QString) - - - 903 - -14 - - - 364 - 3 - - - y_combo fieldChanged(QString) @@ -1622,5 +1675,21 @@ QListWidget::item::selected { + + x_combo + fieldChanged(QString) + x_axis_title + setText(QString) + + + 903 + -14 + + + 364 + 3 + + + From d8df0bfbd0079639f09c87adec1cbaf93a2334c8 Mon Sep 17 00:00:00 2001 From: Florian Neukirchen <99167742+florianneukirchen@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:14:39 +0200 Subject: [PATCH 02/11] Fix ValueError with Plotly >= 6 by moving titlefont to title_font (#373) --- DataPlotly/core/plot_types/plot_type.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/DataPlotly/core/plot_types/plot_type.py b/DataPlotly/core/plot_types/plot_type.py index 4e0bc1f3..8a21bd6e 100644 --- a/DataPlotly/core/plot_types/plot_type.py +++ b/DataPlotly/core/plot_types/plot_type.py @@ -109,11 +109,13 @@ def create_layout(settings): legend={'orientation': settings.layout['legend_orientation']}, title=title, xaxis={ - 'title': x_title, - 'titlefont': { - "size": settings.layout.get('font_xlabel_size', 10), - "color": settings.layout.get('font_xlabel_color', "#000"), - "family": settings.layout.get('font_xlabel_family', "Arial"), + 'title': { + 'text': x_title, + 'font': { + "size": settings.layout.get('font_xlabel_size', 10), + "color": settings.layout.get('font_xlabel_color', "#000"), + "family": settings.layout.get('font_xlabel_family', "Arial"), + }, }, 'autorange': settings.layout['x_inv'], 'range': range_x, @@ -125,11 +127,13 @@ def create_layout(settings): 'gridcolor': settings.layout.get('gridcolor', '#bdbfc0') }, yaxis={ - 'title': y_title, - 'titlefont': { - "size": settings.layout.get('font_ylabel_size', 10), - "color": settings.layout.get('font_ylabel_color', "#000"), - "family": settings.layout.get('font_ylabel_family', "Arial"), + 'title': { + 'text': y_title, + 'font': { + "size": settings.layout.get('font_ylabel_size', 10), + "color": settings.layout.get('font_ylabel_color', "#000"), + "family": settings.layout.get('font_ylabel_family', "Arial"), + }, }, 'autorange': settings.layout['y_inv'], 'range': range_y, From 249b3483eff16db4bf31c521571b0c81cfe2c285 Mon Sep 17 00:00:00 2001 From: Matteo Ghetta Date: Mon, 7 Jul 2025 13:21:29 +0200 Subject: [PATCH 03/11] bump release 4.3.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c4d4e1..b87b29a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +## 4.3.0 - 2025-07-07 + +- New radar plot type thanks to @soaubier Oslandia +- Fix error with title thanks to @florianneukirchen +- Add help link to help menu and metadata thanks to @Gustry + ## 4.2.0 - 2024-10-24 - Fix loading of the plugin when used with `qgis_process`, contribution from @Gustry From 644567cdfa75b280e8dade54f8df1a1fc7ee1f66 Mon Sep 17 00:00:00 2001 From: Matteo Ghetta Date: Tue, 8 Jul 2025 07:40:22 +0200 Subject: [PATCH 04/11] small fix when loading project (regression introduced by radar plot) --- DataPlotly/gui/plot_settings_widget.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index 5d57f1f7..be4b5e78 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -1381,8 +1381,9 @@ def set_settings(self, settings: PlotSettings): # pylint: disable=too-many-stat self.layout_grid_axis_color.setColor( QColor(settings.layout.get('gridcolor') or '#bdbfc0')) self.pie_hole.setValue(settings.properties.get('pie_hole', 0)) - for name in settings.properties.get('y_fields_combo', '').split(", "): - self.y_fields_combo.setItemCheckState(self.y_fields_combo.findText(name), Qt.CheckState.Checked) + if settings.properties.get('y_fields_combo'): + for name in settings.properties.get('y_fields_combo', '').split(", "): + self.y_fields_combo.setItemCheckState(self.y_fields_combo.findText(name), Qt.CheckState.Checked) self.line_combo_threshold.setCurrentText( settings.properties.get('line_combo_threshold', 'Dash Line') ) From 4173cbcbf607294e2d266c743bcc2cbf5110476c Mon Sep 17 00:00:00 2001 From: Matteo Ghetta Date: Tue, 8 Jul 2025 07:41:16 +0200 Subject: [PATCH 05/11] bump release 4.3.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b87b29a1..3fafbdc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 4.3.1 - 2025-07-08 + +- Fix regression when loading projects + ## 4.3.0 - 2025-07-07 - New radar plot type thanks to @soaubier Oslandia From 484c7a9e5692bec533511dbfd50deb810a41ec76 Mon Sep 17 00:00:00 2001 From: soaubier <117665766+soaubier@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:19:18 +0200 Subject: [PATCH 06/11] fix issue 379 (#385) We use "get" to avoid key error when the settings are not in the dictionnary. For instance when the project was saved with an older version of dataplotly that didn't have the settings yet. --- DataPlotly/core/plot_factory.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DataPlotly/core/plot_factory.py b/DataPlotly/core/plot_factory.py index 604277ad..bc110632 100644 --- a/DataPlotly/core/plot_factory.py +++ b/DataPlotly/core/plot_factory.py @@ -164,13 +164,13 @@ def add_source_field_or_expression(field_or_expression): self.settings.properties[ 'z_name'] else (None, False, set()) y_label_expression, _, y_label_attrs = add_source_field_or_expression(self.settings.properties['y_combo_radar_label']) if \ - self.settings.properties[ - 'y_combo_radar_label'] else (None, False, set()) + self.settings.properties.get( + 'y_combo_radar_label') else (None, False, set()) y_fields_expression = QgsExpression("array(" + ", ".join([f'"{field_name}"' for field_name in self.settings.properties['y_fields_combo'].split(", ") ]) + ")") if \ - self.settings.properties[ - 'y_fields_combo'] else None + self.settings.properties.get( + 'y_fields_combo') else None additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression( self.settings.layout['additional_info_expression']) if self.settings.layout[ 'additional_info_expression'] else (None, False, set()) @@ -284,7 +284,7 @@ def add_source_field_or_expression(field_or_expression): y_radar_label = y_label_expression.evaluate(context) if y_radar_label == NULL or y_radar_label is None: continue - elif self.settings.properties['y_combo_radar_label']: + elif self.settings.properties.get('y_combo_radar_label'): y_radar_label = f[self.settings.properties['y_combo_radar_label']] if y_radar_label == NULL or y_radar_label is None: continue From 2deb4785fe587364f1cbd0849a159dd77c5c3d4c Mon Sep 17 00:00:00 2001 From: soaubier <117665766+soaubier@users.noreply.github.com> Date: Thu, 25 Sep 2025 08:26:58 +0200 Subject: [PATCH 07/11] Fix issue 386 (#387) Use the correct length for generating the color list so that each entity gets a unique color. Previously, the number of colors was tied to the number of parameters, causing display issues. --- DataPlotly/core/plot_types/radar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataPlotly/core/plot_types/radar.py b/DataPlotly/core/plot_types/radar.py index 299257ad..9c494b0a 100644 --- a/DataPlotly/core/plot_types/radar.py +++ b/DataPlotly/core/plot_types/radar.py @@ -41,7 +41,7 @@ def create_trace(settings): x = settings.properties["y_fields_combo"].split(", ") # Sample colors from the color scale based on the length of settings.y_radar_values - colors_list = pc.sample_colorscale(settings.properties['color_scale'], np.linspace(0, 1, len(settings.y_radar_values[0]))) + colors_list = pc.sample_colorscale(settings.properties['color_scale'], np.linspace(0, 1, len(settings.y_radar_values))) # List repeating the line type for each element in settings.y_radar_values line_type_list = [settings.properties['line_dash']] * len(settings.y_radar_values) From 0cac67734565485c90b7aa6cb0abf408ddc76d72 Mon Sep 17 00:00:00 2001 From: Sophie Aubier Date: Thu, 13 Mar 2025 18:18:34 +0100 Subject: [PATCH 08/11] add legend style option : size, front, color --- DataPlotly/core/plot_settings.py | 9 +++++++++ DataPlotly/core/plot_types/plot_type.py | 9 ++++++++- DataPlotly/gui/plot_settings_widget.py | 13 +++++++++++++ DataPlotly/ui/dataplotly_dockwidget_base.ui | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/DataPlotly/core/plot_settings.py b/DataPlotly/core/plot_settings.py index b78e643d..54ae584a 100644 --- a/DataPlotly/core/plot_settings.py +++ b/DataPlotly/core/plot_settings.py @@ -51,6 +51,9 @@ class PlotSettings: # pylint: disable=too-many-instance-attributes PROPERTY_FONT_YTICKS_SIZE = 27 PROPERTY_FONT_YTICKS_FAMILY = 28 PROPERTY_FONT_YTICKS_COLOR = 29 + PROPERTY_FONT_LEGEND_SIZE = 27 + PROPERTY_FONT_LEGEND_FAMILY = 28 + PROPERTY_FONT_LEGEND_COLOR = 29 DYNAMIC_PROPERTIES = { PROPERTY_FILTER: QgsPropertyDefinition('filter', 'Feature filter', QgsPropertyDefinition.Boolean), @@ -77,6 +80,9 @@ class PlotSettings: # pylint: disable=too-many-instance-attributes PROPERTY_FONT_YTICKS_SIZE: QgsPropertyDefinition('font_yticks_size', 'Font yticks size', QgsPropertyDefinition.String), PROPERTY_FONT_YTICKS_FAMILY: QgsPropertyDefinition('font_yticks_family', 'Font yticks family', QgsPropertyDefinition.String), PROPERTY_FONT_YTICKS_COLOR: QgsPropertyDefinition('font_yticks_color', 'Font yticks color', QgsPropertyDefinition.ColorWithAlpha), + PROPERTY_FONT_LEGEND_SIZE: QgsPropertyDefinition('font_legend_size', 'Font yticks size', QgsPropertyDefinition.String), + PROPERTY_FONT_LEGEND_FAMILY: QgsPropertyDefinition('font_legend_family', 'Font yticks family', QgsPropertyDefinition.String), + PROPERTY_FONT_LEGEND_COLOR: QgsPropertyDefinition('font_legend_color', 'Font yticks color', QgsPropertyDefinition.ColorWithAlpha), PROPERTY_X_TITLE: QgsPropertyDefinition('x_title', 'X title', QgsPropertyDefinition.String), PROPERTY_Y_TITLE: QgsPropertyDefinition('y_title', 'Y title', QgsPropertyDefinition.String), PROPERTY_Z_TITLE: QgsPropertyDefinition('z_title', 'Z title', QgsPropertyDefinition.String), @@ -169,6 +175,9 @@ def __init__(self, plot_type: str = 'scatter', properties: dict = None, layout: 'font_yticks_size': 10, 'font_yticks_family': "Arial", 'font_yticks_color': "#000000", + 'font_legend_size': 10, + 'font_legend_family': "Arial", + 'font_legend_color': "#000000", 'xaxis': None, 'bar_mode': None, 'x_type': None, diff --git a/DataPlotly/core/plot_types/plot_type.py b/DataPlotly/core/plot_types/plot_type.py index 8a21bd6e..5fcc7fee 100644 --- a/DataPlotly/core/plot_types/plot_type.py +++ b/DataPlotly/core/plot_types/plot_type.py @@ -106,7 +106,14 @@ def create_layout(settings): layout = graph_objs.Layout( showlegend=settings.layout['legend'], - legend={'orientation': settings.layout['legend_orientation']}, + legend={'orientation': settings.layout['legend_orientation'], + 'font': { + 'size': settings.layout.get('font_legend_size', 10), + 'color': settings.layout.get('font_legend_color', "#000"), + 'family': settings.layout.get('font_legend_family', "Arial"), + } + }, + title=title, xaxis={ 'title': { diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index be4b5e78..0de18f95 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -933,14 +933,17 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.font_xticks_label: ['all'], self.font_ylabel_label: ['all'], self.font_yticks_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], + self.font_legend_label: ['all'], self.font_title_style: ['all'], self.font_xlabel_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_xticks_style: ['all'], self.font_ylabel_style: ['all'], self.font_yticks_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], + self.font_legend_style: ['all'], self.font_title_color: ['all'], self.font_xlabel_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.font_xticks_color: ['all'], + self.font_legend_color: ['all'], self.font_ylabel_color: ['all'], self.font_yticks_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], self.x_axis_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], @@ -1197,6 +1200,11 @@ def get_settings(self) -> PlotSettings: # pylint: disable=R0915 self.font_yticks_style.currentFont().pointSize()), 'font_yticks_family': self.font_yticks_style.currentFont().family(), 'font_yticks_color': self.font_yticks_color.color().name(), + 'font_legend_size': max( + self.font_legend_style.currentFont().pixelSize(), + self.font_legend_style.currentFont().pointSize()), + 'font_legend_family': self.font_legend_style.currentFont().family(), + 'font_legend_color': self.font_legend_color.color().name(), 'x_title': self.x_axis_title.text(), 'y_title': self.y_axis_title.text(), 'z_title': self.z_axis_title.text(), @@ -1315,6 +1323,11 @@ def set_settings(self, settings: PlotSettings): # pylint: disable=too-many-stat settings.layout.get('font_yticks_size', 10))) self.font_yticks_color.setColor( QColor(settings.layout.get('font_yticks_color', "#000000"))) + self.font_legend_style.setCurrentFont( + QFont(settings.layout.get('font_legend_style', "Arial"), + settings.layout.get('font_legend_size', 10))) + self.font_legend_color.setColor( + QColor(settings.layout.get('font_legend_color', "#000000"))) self.font_ylabel_style.setCurrentFont( QFont(settings.layout.get('font_ylabel_family', "Arial"), settings.layout.get('font_ylabel_size', 10))) diff --git a/DataPlotly/ui/dataplotly_dockwidget_base.ui b/DataPlotly/ui/dataplotly_dockwidget_base.ui index 7b763325..585101ee 100644 --- a/DataPlotly/ui/dataplotly_dockwidget_base.ui +++ b/DataPlotly/ui/dataplotly_dockwidget_base.ui @@ -1319,6 +1319,13 @@ QListWidget::item::selected {
+ + + + Legend font + + + @@ -1350,6 +1357,13 @@ QListWidget::item::selected { + + + + QgsFontButton::ModeQFont + + + @@ -1367,6 +1381,9 @@ QListWidget::item::selected { + + + From 269cfce5e5d0e126eb9ee6d395dbb67e588548d6 Mon Sep 17 00:00:00 2001 From: Sophie Aubier Date: Wed, 24 Sep 2025 09:30:47 +0200 Subject: [PATCH 09/11] Review : continue the numeration for PROPERTY_FONT_LEGEND --- DataPlotly/core/plot_settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DataPlotly/core/plot_settings.py b/DataPlotly/core/plot_settings.py index 54ae584a..9e7ec930 100644 --- a/DataPlotly/core/plot_settings.py +++ b/DataPlotly/core/plot_settings.py @@ -51,9 +51,9 @@ class PlotSettings: # pylint: disable=too-many-instance-attributes PROPERTY_FONT_YTICKS_SIZE = 27 PROPERTY_FONT_YTICKS_FAMILY = 28 PROPERTY_FONT_YTICKS_COLOR = 29 - PROPERTY_FONT_LEGEND_SIZE = 27 - PROPERTY_FONT_LEGEND_FAMILY = 28 - PROPERTY_FONT_LEGEND_COLOR = 29 + PROPERTY_FONT_LEGEND_SIZE = 30 + PROPERTY_FONT_LEGEND_FAMILY = 31 + PROPERTY_FONT_LEGEND_COLOR = 32 DYNAMIC_PROPERTIES = { PROPERTY_FILTER: QgsPropertyDefinition('filter', 'Feature filter', QgsPropertyDefinition.Boolean), From d41ecf578ab3181391dfb564e3a742ebc93058aa Mon Sep 17 00:00:00 2001 From: Sophie Aubier Date: Wed, 24 Sep 2025 09:37:08 +0200 Subject: [PATCH 10/11] Review: add font_legend_color & font_legend_style to the default colors & font --- DataPlotly/gui/plot_settings_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index 0de18f95..5adabbd1 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -323,6 +323,7 @@ def __init__(self, mode=MODE_CANVAS, parent=None, override_iface=None, message_b self.font_xticks_color.setColor(QColor('#000000')) self.font_ylabel_color.setColor(QColor('#000000')) self.font_yticks_color.setColor(QColor('#000000')) + self.font_legend_color.setColor(QColor('#000000')) # default fonts self.font_title_style.setCurrentFont(QFont('Arial', 10)) @@ -330,6 +331,7 @@ def __init__(self, mode=MODE_CANVAS, parent=None, override_iface=None, message_b self.font_xticks_style.setCurrentFont(QFont('Arial', 10)) self.font_ylabel_style.setCurrentFont(QFont('Arial', 10)) self.font_yticks_style.setCurrentFont(QFont('Arial', 10)) + self.font_legend_style.setCurrentFont(QFont('Arial', 10)) # set range of axis min/max spin boxes self.x_axis_min.setRange(sys.float_info.max * -1, sys.float_info.max) From 8cb6faaeaa94c83727a2c4e8f8e59706018f2771 Mon Sep 17 00:00:00 2001 From: Sophie Aubier Date: Tue, 30 Sep 2025 14:49:46 +0200 Subject: [PATCH 11/11] manage the label and title customization --- DataPlotly/core/plot_types/polar.py | 17 ++++++++++++++++- DataPlotly/gui/plot_settings_widget.py | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/DataPlotly/core/plot_types/polar.py b/DataPlotly/core/plot_types/polar.py index 963e1ec9..794e80e4 100644 --- a/DataPlotly/core/plot_types/polar.py +++ b/DataPlotly/core/plot_types/polar.py @@ -59,5 +59,20 @@ def create_layout(settings): layout = super(PolarChartFactory, PolarChartFactory).create_layout(settings) layout['polar'] = settings.layout['polar'] - + layout['polar'].update({ + 'radialaxis': { + 'tickfont':{ + "size": settings.layout.get('font_xticks_size',30), + "color": settings.layout.get('font_xticks_color',"#00000"), + "family": settings.layout.get('font_xticks_family', "Arial"), + } + }, + 'angularaxis':{ + 'tickfont':{ + "size": settings.layout.get('font_yticks_size',30), + "color": settings.layout.get('font_yticks_color',"#00000"), + "family": settings.layout.get('font_yticks_family', "Arial") + } + } + }) return layout diff --git a/DataPlotly/gui/plot_settings_widget.py b/DataPlotly/gui/plot_settings_widget.py index 5adabbd1..81155161 100644 --- a/DataPlotly/gui/plot_settings_widget.py +++ b/DataPlotly/gui/plot_settings_widget.py @@ -931,23 +931,23 @@ def refreshWidgets(self): # pylint: disable=too-many-statements,too-many-branch self.plot_title_line: ['all'], self.plot_title_defined_button: ['all'], self.font_title_label: ['all'], - self.font_xlabel_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], - self.font_xticks_label: ['all'], - self.font_ylabel_label: ['all'], - self.font_yticks_label: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], + self.font_xlabel_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_xticks_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar','radar', 'violin'], + self.font_ylabel_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_yticks_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar','radar', 'violin'], self.font_legend_label: ['all'], self.font_title_style: ['all'], - self.font_xlabel_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], - self.font_xticks_style: ['all'], - self.font_ylabel_style: ['all'], - self.font_yticks_style: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], + self.font_xlabel_style: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_xticks_style: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar', 'radar', 'violin'], + self.font_ylabel_style: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_yticks_style: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar','radar', 'violin'], self.font_legend_style: ['all'], self.font_title_color: ['all'], - self.font_xlabel_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], - self.font_xticks_color: ['all'], + self.font_xlabel_color: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_xticks_color: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar','radar', 'violin'], self.font_legend_color: ['all'], - self.font_ylabel_color: ['all'], - self.font_yticks_color: ['scatter', 'bar', 'box', 'pie', '2dhistogram','histogram', 'polar','ternary', 'contour', 'violin'], + self.font_ylabel_color: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'violin'], + self.font_yticks_color: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'polar','radar', 'violin'], self.x_axis_label: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], self.x_axis_title: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'], self.x_axis_title_defined_button: ['scatter', 'bar', 'box', 'histogram', '2dhistogram', 'ternary', 'violin'],