Skip to content

Commit

Permalink
Line plot: Customizable figure
Browse files Browse the repository at this point in the history
  • Loading branch information
VesnaT committed Jul 3, 2020
1 parent 57889a3 commit a26dba7
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 24 deletions.
173 changes: 150 additions & 23 deletions Orange/widgets/visualize/owlineplot.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import List
from xml.sax.saxutils import escape

import numpy as np
Expand Down Expand Up @@ -84,8 +85,9 @@ class LinePlotStyle:
SELECTION_LINE_COLOR = QColor(Qt.black)
SELECTION_LINE_WIDTH = 2

UNSELECTED_LINE_WIDTH = 1
UNSELECTED_LINE_ALPHA = 100
UNSELECTED_LINE_ALPHA_SEL = 50
UNSELECTED_LINE_ALPHA_SEL = 50 # unselected lines, when selection exists

SELECTED_LINE_WIDTH = 3
SELECTED_LINE_ALPHA = 170
Expand Down Expand Up @@ -189,6 +191,11 @@ def reset(self):


class ParameterSetter(Setter):
MEAN_LABEL = "Mean"
LINE_LABEL = "Lines"
SEL_LINE_LABEL = "Selected lines"
RANGE_LABEL = "Range"
SEL_RANGE_LABEL = "Selected range"
initial_settings = {
Setter.LABELS_BOX: {
Setter.FONT_FAMILY_LABEL: Updater.FONT_FAMILY_SETTING,
Expand All @@ -201,9 +208,104 @@ class ParameterSetter(Setter):
Setter.TITLE_LABEL: {Setter.TITLE_LABEL: ("", "")},
Setter.X_AXIS_LABEL: {Setter.TITLE_LABEL: ("", "")},
Setter.Y_AXIS_LABEL: {Setter.TITLE_LABEL: ("", "")},
},
Setter.PLOT_BOX: {
MEAN_LABEL: {
Updater.WIDTH_LABEL: (range(1, 15), LinePlotStyle.MEAN_WIDTH),
Updater.STYLE_LABEL: (list(Updater.LINE_STYLES),
Updater.DEFAULT_LINE_STYLE),
},
LINE_LABEL: {
Updater.WIDTH_LABEL: (range(1, 15),
LinePlotStyle.UNSELECTED_LINE_WIDTH),
Updater.STYLE_LABEL: (list(Updater.LINE_STYLES),
Updater.DEFAULT_LINE_STYLE),
Updater.ALPHA_LABEL: (range(0, 255, 5),
LinePlotStyle.UNSELECTED_LINE_ALPHA),
Updater.ANTIALIAS_LABEL: (None, True),
},
SEL_LINE_LABEL: {
Updater.WIDTH_LABEL: (range(1, 15),
LinePlotStyle.SELECTED_LINE_WIDTH),
Updater.STYLE_LABEL: (list(Updater.LINE_STYLES),
Updater.DEFAULT_LINE_STYLE),
Updater.ALPHA_LABEL: (range(0, 255, 5),
LinePlotStyle.SELECTED_LINE_ALPHA),
Updater.ANTIALIAS_LABEL: (None, False),
},
RANGE_LABEL: {
Updater.ALPHA_LABEL: (range(0, 255, 5),
LinePlotStyle.RANGE_ALPHA),
},
SEL_RANGE_LABEL: {
Updater.ALPHA_LABEL: (range(0, 255, 5),
LinePlotStyle.SELECTED_RANGE_ALPHA),
},
}
}

def __init__(self):
self.mean_settings = {
Updater.WIDTH_LABEL: LinePlotStyle.MEAN_WIDTH,
Updater.STYLE_LABEL: Updater.DEFAULT_LINE_STYLE,
}
self.line_settings = {
Updater.WIDTH_LABEL: LinePlotStyle.UNSELECTED_LINE_WIDTH,
Updater.ALPHA_LABEL: LinePlotStyle.UNSELECTED_LINE_ALPHA,
Updater.STYLE_LABEL: Updater.DEFAULT_LINE_STYLE,
Updater.ANTIALIAS_LABEL: True,
}
self.sel_line_settings = {
Updater.WIDTH_LABEL: LinePlotStyle.SELECTED_LINE_WIDTH,
Updater.ALPHA_LABEL: LinePlotStyle.SELECTED_LINE_ALPHA,
Updater.STYLE_LABEL: Updater.DEFAULT_LINE_STYLE,
Updater.ANTIALIAS_LABEL: False,
}
self.range_settings = {
Updater.ALPHA_LABEL: LinePlotStyle.RANGE_ALPHA,
}
self.sel_range_settings = {
Updater.ALPHA_LABEL: LinePlotStyle.SELECTED_RANGE_ALPHA,
}
super().__init__()

def update_setters(self):
def update_mean(**settings):
self.mean_settings.update(**settings)
Updater.update_lines(self.mean_lines_items, **self.mean_settings)

def update_lines(**settings):
self.line_settings.update(**settings)
Updater.update_lines(self.lines_items, **self.line_settings)

def update_sel_lines(**settings):
self.sel_line_settings.update(**settings)
Updater.update_lines(self.sel_lines_items, **self.sel_line_settings)

def _update_brush(items, **settings):
for item in items:
brush = item.brush()
color = brush.color()
color.setAlpha(settings[Updater.ALPHA_LABEL])
brush.setColor(color)
item.setBrush(brush)

def update_range(**settings):
self.range_settings.update(**settings)
_update_brush(self.range_items, **settings)

def update_sel_range(**settings):
self.sel_range_settings.update(**settings)
_update_brush(self.sel_range_items, **settings)

self._setters[self.PLOT_BOX] = {
self.MEAN_LABEL: update_mean,
self.LINE_LABEL: update_lines,
self.SEL_LINE_LABEL: update_sel_lines,
self.RANGE_LABEL: update_range,
self.SEL_RANGE_LABEL: update_sel_range,
}

@property
def title_item(self):
return self.getPlotItem().titleLabel
Expand All @@ -216,10 +318,32 @@ def axis_items(self):
def legend_items(self):
return self.legend.items

@property
def mean_lines_items(self):
return [group.mean for group in self.groups]

@property
def lines_items(self):
return [group.profiles for group in self.groups]

@property
def sel_lines_items(self):
return [group.sel_profiles for group in self.groups] + \
[group.sub_profiles for group in self.groups]

@property
def range_items(self):
return [group.range for group in self.groups]

@property
def sel_range_items(self):
return [group.sel_range for group in self.groups]


# Customizable plot widget
class LinePlotGraph(pg.PlotWidget, ParameterSetter):
def __init__(self, parent):
self.groups: List[ProfileGroup] = []
self.bottom_axis = BottomAxisItem(orientation="bottom")
self.bottom_axis.setLabel("")
left_axis = AxisItem(orientation="left")
Expand Down Expand Up @@ -270,6 +394,7 @@ def reset(self):
self.clear()
self.getAxis('bottom').set_ticks(None)
self.legend.hide()
self.groups = []

def select_button_clicked(self):
self.view_box.set_graph_state(SELECT)
Expand Down Expand Up @@ -322,20 +447,19 @@ def __create_curves(self):

def _get_profiles_curve(self):
x, y, con = self.__get_disconnected_curve_data(self.y_data)
color = QColor(self.color)
color.setAlpha(LinePlotStyle.UNSELECTED_LINE_ALPHA)
pen = self.make_pen(color)
return pg.PlotCurveItem(x=x, y=y, connect=con, pen=pen, antialias=True)
pen = self.make_pen(self.color)
curve = pg.PlotCurveItem(x=x, y=y, connect=con, pen=pen)
Updater.update_lines([curve], **self.graph.line_settings)
return curve

def _get_sel_profiles_curve(self):
color = QColor(self.color)
color.setAlpha(LinePlotStyle.SELECTED_LINE_ALPHA)
pen = self.make_pen(color, LinePlotStyle.SELECTED_LINE_WIDTH)
return pg.PlotCurveItem(x=None, y=None, pen=pen, antialias=False)
curve = pg.PlotCurveItem(x=None, y=None, pen=self.make_pen(self.color))
Updater.update_lines([curve], **self.graph.sel_line_settings)
return curve

def _get_range_curve(self):
color = QColor(self.color)
color.setAlpha(LinePlotStyle.RANGE_ALPHA)
color.setAlpha(self.graph.range_settings[Updater.ALPHA_LABEL])
bottom, top = nanmin(self.y_data, axis=0), nanmax(self.y_data, axis=0)
return pg.FillBetweenItem(
pg.PlotDataItem(x=self.x_data, y=bottom),
Expand All @@ -344,15 +468,15 @@ def _get_range_curve(self):

def _get_sel_range_curve(self):
color = QColor(self.color)
color.setAlpha(LinePlotStyle.SELECTED_RANGE_ALPHA)
color.setAlpha(self.graph.sel_range_settings[Updater.ALPHA_LABEL])
curve1 = curve2 = pg.PlotDataItem(x=self.x_data, y=self.__mean)
return pg.FillBetweenItem(curve1, curve2, brush=color)

def _get_mean_curve(self):
pen = self.make_pen(self.color.darker(LinePlotStyle.MEAN_DARK_FACTOR),
LinePlotStyle.MEAN_WIDTH)
return pg.PlotCurveItem(x=self.x_data, y=self.__mean,
pen=pen, antialias=True)
pen = self.make_pen(self.color.darker(LinePlotStyle.MEAN_DARK_FACTOR))
curve = pg.PlotCurveItem(x=self.x_data, y=self.__mean, pen=pen)
Updater.update_lines([curve], **self.graph.mean_settings)
return curve

def _get_error_bar(self):
std = nanstd(self.y_data, axis=0)
Expand Down Expand Up @@ -402,11 +526,12 @@ def set_visible_error(self, show_error=True, **_):

def update_profiles_color(self, selection):
color = QColor(self.color)
alpha = LinePlotStyle.UNSELECTED_LINE_ALPHA if not selection \
else LinePlotStyle.UNSELECTED_LINE_ALPHA_SEL
alpha = self.graph.line_settings[Updater.ALPHA_LABEL] \
if not selection else LinePlotStyle.UNSELECTED_LINE_ALPHA_SEL
color.setAlpha(alpha)
x, y = self.profiles.getData()
self.profiles.setData(x=x, y=y, pen=self.make_pen(color))
pen = self.profiles.opts["pen"]
pen.setColor(color)
self.profiles.setPen(pen)

def update_sel_profiles(self, y_data):
x, y, connect = self.__get_disconnected_curve_data(y_data) \
Expand All @@ -415,10 +540,10 @@ def update_sel_profiles(self, y_data):

def update_sel_profiles_color(self, subset):
color = QColor(Qt.black) if subset else QColor(self.color)
color.setAlpha(LinePlotStyle.SELECTED_LINE_ALPHA)
pen = self.make_pen(color, LinePlotStyle.SELECTED_LINE_WIDTH)
x, y = self.sel_profiles.getData()
self.sel_profiles.setData(x=x, y=y, pen=pen)
color.setAlpha(self.graph.sel_line_settings[Updater.ALPHA_LABEL])
pen = self.sel_profiles.opts["pen"]
pen.setColor(color)
self.sel_profiles.setPen(pen)

def update_sub_profiles(self, y_data):
x, y, connect = self.__get_disconnected_curve_data(y_data) \
Expand Down Expand Up @@ -674,12 +799,14 @@ def plot_groups(self):
group_data = self.data[indices, self.graph_variables]
self._plot_group(group_data, indices, index)
self.graph.update_legend(self.group_var)
self.graph.groups = self.__groups
self.graph.view_box.add_profiles(data.X)

def _remove_groups(self):
for group in self.__groups:
group.remove_items()
self.graph.view_box.remove_profiles()
self.graph.groups = []
self.__groups = []

def _plot_group(self, data, indices, index=None):
Expand Down
43 changes: 42 additions & 1 deletion Orange/widgets/visualize/utils/customizableplot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
from typing import Tuple, List, Dict, Callable, Iterable
from typing import Tuple, List, Dict, Iterable

from AnyQt.QtCore import Qt
from AnyQt.QtGui import QFont, QFontDatabase
from AnyQt.QtWidgets import QApplication

Expand Down Expand Up @@ -71,6 +72,21 @@ class Updater:
IS_ITALIC_LABEL: (None, False)
}

WIDTH_LABEL, ALPHA_LABEL, STYLE_LABEL, ANTIALIAS_LABEL = \
"Width", "Opacity", "Style", "Antialias"
LINE_STYLES = {"Solid line": Qt.SolidLine,
"Dash line": Qt.DashLine,
"Dot line": Qt.DotLine,
"Dash dot line": Qt.DashDotLine,
"Dash dot dot line": Qt.DashDotDotLine}
DEFAULT_LINE_STYLE = "Solid line"
LINE_SETTING: SettingsType = {
WIDTH_LABEL: (range(1, 15), 1),
ALPHA_LABEL: (range(0, 255, 5), 255),
STYLE_LABEL: (list(LINE_STYLES), DEFAULT_LINE_STYLE),
ANTIALIAS_LABEL: (None, False),
}

@staticmethod
def update_plot_title_text(title_item: pg.LabelItem, text: str):
title_item.text = text
Expand Down Expand Up @@ -164,11 +180,36 @@ def change_font(font: QFont, settings: _SettingType) -> QFont:
font.setItalic(italic)
return font

@staticmethod
def update_lines(items: List[pg.PlotCurveItem], **settings: _SettingType):
for item in items:
antialias = settings.get(Updater.ANTIALIAS_LABEL)
if antialias is not None:
item.setData(item.xData, item.yData, antialias=antialias)

pen = item.opts["pen"]
alpha = settings.get(Updater.ALPHA_LABEL)
if alpha is not None:
color = pen.color()
color.setAlpha(alpha)
pen.setColor(color)

style = settings.get(Updater.STYLE_LABEL)
if style is not None:
pen.setStyle(Updater.LINE_STYLES[style])

width = settings.get(Updater.WIDTH_LABEL)
if width is not None:
pen.setWidth(width)

item.setPen(pen)


class BaseParameterSetter:
""" Subclass to add 'setter' functionality to a plot. """
LABELS_BOX = "Fonts"
ANNOT_BOX = "Annotations"
PLOT_BOX = "Figure"

FONT_FAMILY_LABEL = "Font family"
AXIS_TITLE_LABEL = "Axis title"
Expand Down

0 comments on commit a26dba7

Please sign in to comment.