Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Reorganize continuous palettes #4305

Merged
merged 22 commits into from
Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c27239e
colorpalettes: Add module, deprecate colopalette and colorbrewer
janezd Jan 26, 2020
483e0a1
Variable: Remove colors
janezd Jan 26, 2020
a6de10b
gui: Add palette_combo_box
janezd Jan 26, 2020
ceb74bf
OWColor: Port widget to new palettes
janezd Jan 26, 2020
34f4251
OWPaintData: Port to new palettes
janezd Jan 26, 2020
4c3845b
Scatterplot base: Port to new palettes
janezd Jan 26, 2020
8f78c7b
OWFeatureStatistics: Port to new palettes
janezd Jan 26, 2020
f9091f9
OWCalibrationPlot, OWLiftCurve, OWRocAnalysis: Port to new palettes
janezd Jan 26, 2020
7018019
OWDistanceMap: Port to new palettes
janezd Jan 26, 2020
e139774
OWDistanceMatric: Port to new palettes
janezd Jan 26, 2020
287278a
OWHeatMap: Port to new palettes
janezd Jan 26, 2020
19a51c6
OWSom: Port to new palettes
janezd Jan 26, 2020
5216ee2
OWHierarchicalClustering: Port to new palettes
janezd Jan 26, 2020
43a0e49
OWTreeViewer: Port to new palettes
janezd Jan 26, 2020
6bc23f8
OWVennDiagram: Port to new palettes
janezd Jan 26, 2020
d37a7e7
PythagorasTreeViewer: Port to new palettes
janezd Jan 26, 2020
dcee4d9
owlegend.ContinuousLegendItem: Port to new palettes
janezd Jan 31, 2020
340b932
OWHeatMap: Decide about centering based on palette type
janezd Feb 1, 2020
9b1b1b8
OWColor: Update documentation
janezd Feb 1, 2020
b513f5a
Update documentation for Color
ajdapretnar Feb 3, 2020
bee3df1
Trick build_docs to ignore explicitly unindexed images
ajdapretnar Feb 3, 2020
e5b6949
Color palettes: Fixes related to review
janezd Feb 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis/build_doc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ images="$(git diff --name-only origin/master..HEAD |
echo "Checking if images are indexed:"
while read image; do
[ -f "$image" ] || continue
if [[ "$image" == *"_unindexed"* ]]; then
continue
fi
imtype=$(identify -verbose "$image" | awk '/^ *Type: /{ print $2 }')
echo "$image $imtype"
if ! echo "$imtype" | grep -Eq '(Palette|Grayscale)'; then
Expand Down
68 changes: 17 additions & 51 deletions Orange/data/tests/test_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,31 +190,6 @@ def test_repr(self):
repr(var),
"DiscreteVariable(name='a', values=['1', '2', '3', '4', '5', '6', '7'])")

@unittest.skipUnless(is_on_path("PyQt4") or is_on_path("PyQt5"), "PyQt is not importable")
def test_colors(self):
var = DiscreteVariable.make("a", values=["F", "M"])
self.assertIsNone(var._colors)
self.assertEqual(var.colors.shape, (2, 3))
self.assertFalse(var.colors.flags.writeable)

var.colors = np.arange(6).reshape((2, 3))
np.testing.assert_almost_equal(var.colors, [[0, 1, 2], [3, 4, 5]])
self.assertFalse(var.colors.flags.writeable)
with self.assertRaises(ValueError):
var.colors[0] = [42, 41, 40]

var = DiscreteVariable.make("x", values=["A", "B"])
var.attributes["colors"] = ['#0a0b0c', '#0d0e0f']
np.testing.assert_almost_equal(var.colors, [[10, 11, 12], [13, 14, 15]])

# Test ncolors adapts to nvalues
var = DiscreteVariable.make('foo', values=['d', 'r'])
self.assertEqual(len(var.colors), 2)
var.add_value('e')
self.assertEqual(len(var.colors), 3)
var.add_value('k')
self.assertEqual(len(var.colors), 4)

def test_no_nonstringvalues(self):
self.assertRaises(TypeError, DiscreteVariable, "foo", values=["a", 42])
a = DiscreteVariable("foo", values=["a", "b", "c"])
Expand Down Expand Up @@ -394,6 +369,23 @@ def varcls_modified(self, name):
var.ordered = True
return var

def test_copy_checks_len_values(self):
var = DiscreteVariable("gender", values=["F", "M"])
self.assertEqual(var.values, ["F", "M"])

self.assertRaises(ValueError, var.copy, values=["F", "M", "N"])
self.assertRaises(ValueError, var.copy, values=["F"])
self.assertRaises(ValueError, var.copy, values=[])

var2 = var.copy()
self.assertEqual(var2.values, ["F", "M"])

var2 = var.copy(values=None)
self.assertEqual(var2.values, ["F", "M"])

var2 = var.copy(values=["W", "M"])
self.assertEqual(var2.values, ["W", "M"])


@variabletest(ContinuousVariable)
class TestContinuousVariable(VariableTest):
Expand Down Expand Up @@ -423,17 +415,6 @@ def test_adjust_decimals(self):
a.val_from_str_add("5.1234")
self.assertEqual(a.str_val(4.65432), "4.6543")

def test_colors(self):
a = ContinuousVariable("a")
self.assertEqual(a.colors, ((0, 0, 255), (255, 255, 0), False))

a = ContinuousVariable("a")
a.attributes["colors"] = ['#010203', '#040506', True]
self.assertEqual(a.colors, ((1, 2, 3), (4, 5, 6), True))

a.colors = ((3, 2, 1), (6, 5, 4), True)
self.assertEqual(a.colors, ((3, 2, 1), (6, 5, 4), True))

def varcls_modified(self, name):
var = super().varcls_modified(name)
var.number_of_decimals = 5
Expand Down Expand Up @@ -621,21 +602,6 @@ def test_make_proxy_cont(self):
self.assertEqual(hash(abc), hash(abc1))
self.assertEqual(hash(abc1), hash(abc2))

def test_proxy_has_separate_colors(self):
abc = ContinuousVariable("abc")
abc1 = abc.make_proxy()
abc2 = abc1.make_proxy()

original_colors = abc.colors
red_to_green = (255, 0, 0), (0, 255, 0), False
blue_to_red = (0, 0, 255), (255, 0, 0), False

abc1.colors = red_to_green
abc2.colors = blue_to_red
self.assertEqual(abc.colors, original_colors)
self.assertEqual(abc1.colors, red_to_green)
self.assertEqual(abc2.colors, blue_to_red)

def test_proxy_has_separate_attributes(self):
image = StringVariable("image")
image1 = image.make_proxy()
Expand Down
48 changes: 7 additions & 41 deletions Orange/data/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import scipy.sparse as sp

from Orange.data import _variable
from Orange.util import Registry, hex_to_color, Reprable,\
OrangeDeprecationWarning
from Orange.util import Registry, Reprable, OrangeDeprecationWarning


__all__ = ["Unknown", "MISSING_VALUES", "make_variable", "is_discrete_values",
"Value", "Variable", "ContinuousVariable", "DiscreteVariable",
Expand Down Expand Up @@ -323,20 +323,11 @@ def __init__(self, name="", compute_value=None, *, sparse=False):
self.source_variable = None
self.sparse = sparse
self.attributes = {}
self._colors = None

@property
def name(self):
return self._name

@property
def colors(self): # unreachable; pragma: no cover
return self._colors

@colors.setter
def colors(self, value):
self._colors = value

def make_proxy(self):
"""
Copy the variable and set the master to `self.master` or to `self`.
Expand Down Expand Up @@ -519,17 +510,6 @@ def format_str(self):
def format_str(self, value):
self._format_str = value

@Variable.colors.getter
def colors(self):
if self._colors is not None:
return self._colors
try:
col1, col2, black = self.attributes["colors"]
return (hex_to_color(col1), hex_to_color(col2), black)
except (KeyError, ValueError):
# User-provided colors were not available or invalid
return ((0, 0, 255), (255, 255, 0), False)

# noinspection PyAttributeOutsideInit
@number_of_decimals.setter
def number_of_decimals(self, x):
Expand Down Expand Up @@ -696,22 +676,6 @@ def mapper(value, col_idx=None):

return mapper

@Variable.colors.getter
def colors(self):
if self._colors is not None:
colors = np.array(self._colors)
elif not self.values:
colors = np.zeros((0, 3)) # to match additional colors in vstacks
else:
from Orange.widgets.utils.colorpalette import ColorPaletteGenerator
default = tuple(ColorPaletteGenerator.palette(self))
colors = self.attributes.get('colors', ())
colors = tuple(hex_to_color(color) for color in colors) \
+ default[len(colors):]
colors = np.array(colors)
colors.flags.writeable = False
return colors

def to_val(self, s):
"""
Convert the given argument to a value of the variable (`float`).
Expand Down Expand Up @@ -744,7 +708,6 @@ def add_value(self, s):
if not isinstance(s, str):
raise TypeError("values of DiscreteVariables must be strings")
self.values.append(s)
self._colors = None

def val_from_str_add(self, s):
"""
Expand Down Expand Up @@ -787,9 +750,12 @@ def __reduce__(self):
self.values, self.ordered), \
__dict__

def copy(self, compute_value=None, *, name=None, **_):
def copy(self, compute_value=None, *, name=None, values=None, **_):
if values is not None and len(values) != len(self.values):
raise ValueError(
"number of values must match the number of original values")
return super().copy(compute_value=compute_value, name=name,
values=self.values, ordered=self.ordered)
values=values or self.values, ordered=self.ordered)


class StringVariable(Variable):
Expand Down
Loading