Repository: biolab/orange3

Issue:
Add legend to PCA widget
**Is your feature request related to a problem? Please describe.**
The PCA widget shows some curves to see how well the variance is explained, but it might not be clear for everyone what the two colors are. 

**Describe the solution you'd like**
I think a small legend would be very useful on the plot.

**Additional context**
![image](https://user-images.githubusercontent.com/6304794/67747547-a3cd6800-fa31-11e9-8b5b-9f81c1486388.png)



Discussion:

Resolving patch:
diff --git a/Orange/widgets/unsupervised/owdbscan.py b/Orange/widgets/unsupervised/owdbscan.py
--- a/Orange/widgets/unsupervised/owdbscan.py
+++ b/Orange/widgets/unsupervised/owdbscan.py
@@ -109,7 +109,7 @@ def __init__(self):
         self.plot = SliderGraph(
             x_axis_label="Data items sorted by score",
             y_axis_label="Distance to the k-th nearest neighbour",
-            background="w", callback=self._on_cut_changed
+            callback=self._on_cut_changed
         )
 
         self.mainArea.layout().addWidget(self.plot)
diff --git a/Orange/widgets/unsupervised/owpca.py b/Orange/widgets/unsupervised/owpca.py
--- a/Orange/widgets/unsupervised/owpca.py
+++ b/Orange/widgets/unsupervised/owpca.py
@@ -16,6 +16,7 @@
 
 # Maximum number of PCA components that we can set in the widget
 MAX_COMPONENTS = 100
+LINE_NAMES = ["component variance", "cumulative variance"]
 
 
 class OWPCA(widget.OWWidget):
@@ -83,13 +84,13 @@ def __init__(self):
         self.variance_spin.setSuffix("%")
 
         form.addRow("Components:", self.components_spin)
-        form.addRow("Variance covered:", self.variance_spin)
+        form.addRow("Explained variance:", self.variance_spin)
 
         # Options
         self.options_box = gui.vBox(self.controlArea, "Options")
         self.normalize_box = gui.checkBox(
             self.options_box, self, "normalize",
-            "Normalize data", callback=self._update_normalize
+            "Normalize variables", callback=self._update_normalize
         )
 
         self.maxp_spin = gui.spin(
@@ -194,7 +195,7 @@ def _setup_plot(self):
 
         self.plot.update(
             numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],
-            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos)
+            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)
 
         self._update_axis()
 
diff --git a/Orange/widgets/utils/slidergraph.py b/Orange/widgets/utils/slidergraph.py
--- a/Orange/widgets/utils/slidergraph.py
+++ b/Orange/widgets/utils/slidergraph.py
@@ -24,8 +24,8 @@ class SliderGraph(PlotWidget):
         Plot background color
     """
 
-    def __init__(self, x_axis_label, y_axis_label, callback, background="w"):
-        super().__init__(background=background)
+    def __init__(self, x_axis_label, y_axis_label, callback):
+        super().__init__(background="w")
 
         axis = self.getAxis("bottom")
         axis.setLabel(x_axis_label)
@@ -49,7 +49,8 @@ def __init__(self, x_axis_label, y_axis_label, callback, background="w"):
         self.selection_limit = None
         self.data_increasing = None  # true if data mainly increasing
 
-    def update(self, x, y, colors, cutpoint_x=None, selection_limit=None):
+    def update(self, x, y, colors, cutpoint_x=None, selection_limit=None,
+               names=None):
         """
         Function replots a graph.
 
@@ -57,17 +58,25 @@ def update(self, x, y, colors, cutpoint_x=None, selection_limit=None):
         ----------
         x : np.ndarray
             One-dimensional array with X coordinates of the points
-        y : list
+        y : array-like
             List of np.ndarrays that contains an array of Y values for each
             sequence.
-        colors : list
+        colors : array-like
             List of Qt colors (eg. Qt.red) for each sequence.
         cutpoint_x : int, optional
             A starting cutpoint - the location of the vertical line.
         selection_limit : tuple
             The tuple of two values that limit the range for selection.
+        names : array-like
+            The name of each sequence that shows in the legend, if None
+            legend is not shown.
+        legend_anchor : array-like
+            The anchor of the legend in the graph
         """
         self.clear_plot()
+        if names is None:
+            names = [None] * len(y)
+
         self.sequences = y
         self.x = x
         self.selection_limit = selection_limit
@@ -75,8 +84,16 @@ def update(self, x, y, colors, cutpoint_x=None, selection_limit=None):
         self.data_increasing = [np.sum(d[1:] - d[:-1]) > 0 for d in y]
 
         # plot sequence
-        for s, c in zip(y, colors):
-            self.plot(x, s, pen=mkPen(QColor(c), width=2), antialias=True)
+        for s, c, n, inc in zip(y, colors, names, self.data_increasing):
+            c = QColor(c)
+            self.plot(x, s, pen=mkPen(c, width=2), antialias=True)
+
+            if n is not None:
+                label = TextItem(
+                    text=n, anchor=(0, 1), color=QColor(0, 0, 0, 128))
+                label.setPos(x[-1], s[-1])
+                self._set_anchor(label, len(x) - 1, inc)
+                self.addItem(label)
 
         self._plot_cutpoint(cutpoint_x)
         self.setRange(xRange=(x.min(), x.max()),


Test patch: