diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5c4d4e1..3fafbdc8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,16 @@
## 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
+- 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
diff --git a/DataPlotly/core/plot_factory.py b/DataPlotly/core/plot_factory.py
index 759d2d34..bc110632 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.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.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())
@@ -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.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
+
+ 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..9e7ec930 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 = 30
+ PROPERTY_FONT_LEGEND_FAMILY = 31
+ PROPERTY_FONT_LEGEND_COLOR = 32
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),
@@ -101,6 +107,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 +143,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
@@ -162,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,
@@ -205,6 +221,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 @@
+
+
+
+
diff --git a/DataPlotly/core/plot_types/plot_type.py b/DataPlotly/core/plot_types/plot_type.py
index 4e0bc1f3..5fcc7fee 100644
--- a/DataPlotly/core/plot_types/plot_type.py
+++ b/DataPlotly/core/plot_types/plot_type.py
@@ -106,14 +106,23 @@ 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': 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 +134,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,
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/core/plot_types/radar.py b/DataPlotly/core/plot_types/radar.py
new file mode 100644
index 00000000..9c494b0a
--- /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)))
+ # 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..81155161 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)
@@ -325,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))
@@ -332,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)
@@ -493,11 +493,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 +642,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 +727,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 +771,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 +798,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 +876,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 +901,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 +917,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 +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: ['all'],
- self.font_xticks_label: ['all'],
- self.font_ylabel_label: ['all'],
- self.font_yticks_label: ['all'],
+ 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: ['all'],
- self.font_xticks_style: ['all'],
- self.font_ylabel_style: ['all'],
- self.font_yticks_style: ['all'],
+ 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: ['all'],
- self.font_xticks_color: ['all'],
- self.font_ylabel_color: ['all'],
- self.font_yticks_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: ['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'],
@@ -989,8 +1003,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 +1030,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 +1091,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 +1103,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 +1150,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 +1175,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(),
@@ -1172,6 +1202,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(),
@@ -1190,7 +1225,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,
@@ -1291,6 +1325,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)))
@@ -1357,13 +1396,22 @@ 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))
+ 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')
+ )
+ 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 +1419,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 +1471,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 +1651,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 +1659,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..585101ee 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,260 +509,255 @@ 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
- -
-
-
- -
-
-
- Marker color
-
-
-
- -
-
+
-
+
- Color scale
-
+ Contour type
+
- -
-
+
-
+
+
-
+
+
+ -
+
- ...
+ Show lines
+
+
+ true
- -
-
+
- -
-
+
-
+
+
+ -
+
- Point type
+ ...
- -
-
+
-
+
- Show mean line
-
-
- true
+ Label text position
- -
-
-
-
-
-
- -
-
+
-
+
- Visible
+ Threshold
true
- -
-
-
- Invert color
-
-
-
-
+ -
+
- -
-
+
-
+
- Outliers
+ Marker size
- -
-
-
- Qt::StrongFocus
-
-
+
-
+
- -
-
-
- Manual bin size
+
-
+
+
+
+ 0
+ 0
+
- -
-
+
-
+
- Legend title
+ Bar orientation
- -
-
+
-
+
- Color scale
+ Violin side
- -
-
-
- -
-
+
-
+
- Contour type
+ Opacity
- -
-
+
-
+
- -
-
+
-
+
- Hover tooltip
+ Threshold line type
- -
-
-
-
-
-
- -
-
-
- Show lines
-
-
- true
+
-
+
+
+ Qt::StrongFocus
-
+ -
+
- -
-
-
-
-
-
- true
-
+
-
+
- Invert histogram direction
+ Line type
- -
-
-
- true
-
+
-
+
- Cumulative histogram
+ Normalization
-
+ -
+
- -
-
+
-
+
+
+ -
+
- ...
+ Manual bin size
- -
-
+
-
+
- -
-
+
-
+
- ...
+ Pie hole
- -
-
-
- Opacity
+
-
+
+
+ false
+
+
+ 1000
+
+
+ 10
- -
-
-
- -
-
+
-
+
0
@@ -751,116 +766,165 @@ QListWidget::item::selected {
- -
-
+
-
+
+
+ Fill
+
+
- -
-
-
- false
+
-
+
+
+ Outliers
+
+
+
+ -
+
+
+ Stroke width
+
+
+
+ -
+
+
+ ...
+
+
+ -
+
- 1000
+ 0.950000000000000
+
+
+ 0.050000000000000
- 10
+ 0.000000000000000
+
+
+ false
- -
-
+
-
+
+
+ Color scale
+
+
- -
-
+
-
+
+
+ ...
+
+
- -
-
+
-
+
- -
-
+
-
+
- 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
@@ -1255,6 +1319,13 @@ QListWidget::item::selected {
+ -
+
+
+ Legend font
+
+
+
-
@@ -1286,6 +1357,13 @@ QListWidget::item::selected {
+ -
+
+
+ QgsFontButton::ModeQFont
+
+
+
-
@@ -1303,6 +1381,9 @@ QListWidget::item::selected {
-
+ -
+
+
-
@@ -1499,6 +1580,11 @@ QListWidget::item::selected {
+
+ QgsCheckableComboBox
+ QComboBox
+
+
QgsCollapsibleGroupBox
QGroupBox
@@ -1558,22 +1644,6 @@ QListWidget::item::selected {
-
- x_combo
- fieldChanged(QString)
- x_axis_title
- setText(QString)
-
-
- 903
- -14
-
-
- 364
- 3
-
-
-
y_combo
fieldChanged(QString)
@@ -1622,5 +1692,21 @@ QListWidget::item::selected {
+
+ x_combo
+ fieldChanged(QString)
+ x_axis_title
+ setText(QString)
+
+
+ 903
+ -14
+
+
+ 364
+ 3
+
+
+