diff --git a/.gitignore b/.gitignore index 8c9de33a3..a908c18ef 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ htmlcov/* # check_pylint_diff .pylint_cache + +# dask lcok files +*.lock +*.dirlock diff --git a/orangecontrib/spectroscopy/__init__.py b/orangecontrib/spectroscopy/__init__.py index 33b873f8d..ed0005f3c 100644 --- a/orangecontrib/spectroscopy/__init__.py +++ b/orangecontrib/spectroscopy/__init__.py @@ -22,3 +22,14 @@ def get_sample_datasets_dir(): Orange.data.table.dataset_dirs.append(get_sample_datasets_dir()) + + +try: + import dask + import dask.distributed + dask_client = dask.distributed.Client(processes=False, n_workers=2, + set_as_default=False, + dashboard_address=None) +except ImportError: + dask = None + dask_client = None \ No newline at end of file diff --git a/orangecontrib/spectroscopy/preprocess/__init__.py b/orangecontrib/spectroscopy/preprocess/__init__.py index 5cf7779f9..5895e261b 100644 --- a/orangecontrib/spectroscopy/preprocess/__init__.py +++ b/orangecontrib/spectroscopy/preprocess/__init__.py @@ -440,7 +440,7 @@ def __call__(self, data): return np.ones((len(data), len(self.points))) * np.nan interpfn = self.interpfn if interpfn is None: - if self.handle_nans and bottleneck.anynan(ys): + if self.handle_nans and np.any(np.isnan(ys)): if self.kind == "linear": interpfn = interp1d_with_unknowns_numpy else: @@ -472,7 +472,7 @@ def __call__(self, data): self.handle_nans, interpfn=self.interpfn) domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) - return data.from_table(domain, data) + return data.transform(domain) class NotAllContinuousException(Exception): @@ -505,7 +505,7 @@ def __call__(self, data): domain = Orange.data.Domain(self.target.domain.attributes, data.domain.class_vars, data.domain.metas) data = data.transform(domain) - with data.unlocked(data.X): + with data.unlocked_reference(data.X): data.X = X return data diff --git a/orangecontrib/spectroscopy/preprocess/integrate.py b/orangecontrib/spectroscopy/preprocess/integrate.py index 1e03d343a..59f0b09bd 100644 --- a/orangecontrib/spectroscopy/preprocess/integrate.py +++ b/orangecontrib/spectroscopy/preprocess/integrate.py @@ -37,6 +37,11 @@ def draw_info(self, data, common=None): if common is None: common = self.compute_shared(data) x_s, y_s = self.extract_data(data, common) + # draw_info is rarely called. The following assures + # that compute_draw_info will only need to work with numpy + # arrays, which then in turn are the assumed input in pyqtgraph + x_s = np.asarray(x_s) + y_s = np.asarray(y_s) return self.compute_draw_info(x_s, y_s) def extract_data(self, data, common): @@ -85,10 +90,10 @@ def compute_baseline(self, x, y): return edge_baseline(x, y) def compute_integral(self, x, y_s): - y_s = y_s - self.compute_baseline(x, y_s) if np.any(np.isnan(y_s)): # interpolate unknowns as trapz can not handle them y_s, _ = nan_extend_edges_and_interpolate(x, y_s) + y_s = y_s - self.compute_baseline(x, y_s) return np.trapz(y_s, x, axis=1) def compute_draw_info(self, x, ys): @@ -206,7 +211,7 @@ def compute_integral(self, x_s, y_s): return np.zeros((y_s.shape[0],)) * np.nan # avoid whole nan rows whole_nan_rows = np.isnan(y_s).all(axis=1) - y_s[whole_nan_rows] = 0 + y_s[whole_nan_rows, :] = 0 # select positions pos = x_s[bottleneck.nanargmax(y_s, axis=1)] # set unknown results diff --git a/orangecontrib/spectroscopy/preprocess/utils.py b/orangecontrib/spectroscopy/preprocess/utils.py index 4651500a4..863513baa 100644 --- a/orangecontrib/spectroscopy/preprocess/utils.py +++ b/orangecontrib/spectroscopy/preprocess/utils.py @@ -1,4 +1,3 @@ -import bottleneck import numpy as np from Orange.data import Table, Domain from Orange.data.util import SharedComputeValue @@ -7,10 +6,26 @@ from orangecontrib.spectroscopy.data import getx +try: + import dask + import dask.array +except ImportError: + dask = False + + def is_increasing(a): return np.all(np.diff(a) >= 0) +def full_like_type(orig, shape, val): + if isinstance(orig, np.ndarray): + return np.full(shape, val) + elif dask and isinstance(orig, dask.array.Array): + return dask.array.full(shape, val) + else: + raise RuntimeError("Unknown matrix txpe") + + class PreprocessException(Exception): def message(self): @@ -145,12 +160,10 @@ def nan_extend_edges_and_interpolate(xs, X): so that they do not propagate. """ nans = None - if bottleneck.anynan(X): + if np.any(np.isnan(X)): nans = np.isnan(X) - X = X.copy() xs, xsind, mon, X = transform_to_sorted_wavenumbers(xs, X) - fill_edges(X) - X = interp1d_with_unknowns_numpy(xs[xsind], X, xs[xsind]) + X = interp1d_with_unknowns_numpy(xs[xsind], X, xs[xsind], sides=None) X = transform_back_to_features(xsind, mon, X) return X, nans @@ -174,46 +187,57 @@ def transform_back_to_features(xsind, mon, X): def fill_edges_1d(l): """Replace (inplace!) NaN at sides with the closest value""" loc = np.where(~np.isnan(l))[0] - if len(loc): - fi, li = loc[[0, -1]] + try: + fi, li = np.array(loc[[0, -1]]) + except IndexError: + # nothing to do, no valid value + return l + else: l[:fi] = l[fi] l[li + 1:] = l[li] + return l def fill_edges(mat): """Replace (inplace!) NaN at sides with the closest value""" - for l in mat: - fill_edges_1d(l) + for i, l in enumerate(mat): + if dask and isinstance(mat, dask.array.Array): + l = fill_edges_1d(l) + mat[i] = l + else: + fill_edges_1d(l) def remove_whole_nan_ys(x, ys): """Remove whole NaN columns of ys with corresponding x coordinates.""" - whole_nan_columns = bottleneck.allnan(ys, axis=0) + whole_nan_columns = np.isnan(ys).all(axis=0) if np.any(whole_nan_columns): x = x[~whole_nan_columns] ys = ys[:, ~whole_nan_columns] return x, ys -def interp1d_with_unknowns_numpy(x, ys, points, kind="linear"): +def interp1d_with_unknowns_numpy(x, ys, points, kind="linear", sides=np.nan): if kind != "linear": raise NotImplementedError - out = np.zeros((len(ys), len(points)))*np.nan + out = full_like_type(ys, (len(ys), len(points)), np.nan) sorti = np.argsort(x) x = x[sorti] for i, y in enumerate(ys): - y = y[sorti] + # the next line ensures numpy arrays + # for Dask, it would be much more efficient to work with larger sections + y = np.array(y[sorti]) nan = np.isnan(y) xt = x[~nan] yt = y[~nan] # do not interpolate unknowns at the edges if len(xt): # check if all values are removed - out[i] = np.interp(points, xt, yt, left=np.nan, right=np.nan) + out[i] = np.interp(points, xt, yt, left=sides, right=sides) return out def interp1d_with_unknowns_scipy(x, ys, points, kind="linear"): - out = np.zeros((len(ys), len(points)))*np.nan + out = full_like_type(ys, (len(ys), len(points)), np.nan) sorti = np.argsort(x) x = x[sorti] for i, y in enumerate(ys): diff --git a/orangecontrib/spectroscopy/tests/test_interpolate.py b/orangecontrib/spectroscopy/tests/test_interpolate.py index 220af56d2..444e3044f 100644 --- a/orangecontrib/spectroscopy/tests/test_interpolate.py +++ b/orangecontrib/spectroscopy/tests/test_interpolate.py @@ -1,51 +1,73 @@ import unittest import numpy as np + +try: + import dask + from Orange.tests.test_dasktable import temp_dasktable +except ImportError: + dask = None + import Orange + from orangecontrib.spectroscopy.preprocess import Interpolate, \ interp1d_with_unknowns_numpy, interp1d_with_unknowns_scipy, \ - interp1d_wo_unknowns_scipy, InterpolateToDomain, NotAllContinuousException + interp1d_wo_unknowns_scipy, InterpolateToDomain, NotAllContinuousException, \ + nan_extend_edges_and_interpolate from orangecontrib.spectroscopy.data import getx +from orangecontrib.spectroscopy.tests.util import spectra_table class TestInterpolate(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = Orange.data.Table("iris")[:5].copy() + cls.collagen = Orange.data.Table("collagen.csv")[:5] + cls.titanic = Orange.data.Table("titanic") + ys = np.arange(16, dtype=float).reshape(4, 4) + np.fill_diagonal(ys, np.nan) + cls.range16 = spectra_table([0, 1, 2, 3], X=ys) + def test_nofloatname(self): - data = Orange.data.Table("iris") + data = self.iris interpolated = Interpolate([0.5])(data) + self.assertIsInstance(interpolated, type(data)) av1 = interpolated.X.ravel() av2 = data.X[:, :2].mean(axis=1) np.testing.assert_allclose(av1, av2) def test_floatname(self): - data = Orange.data.Table("collagen.csv") + data = self.collagen f1, f2 = 20, 21 c1, c2 = float(data.domain.attributes[f1].name), \ float(data.domain.attributes[f2].name) avg = (c1 + c2)/2 interpolated = Interpolate([avg])(data) + self.assertIsInstance(interpolated, type(data)) av1 = interpolated.X.ravel() av2 = data.X[:, [20,21]].mean(axis=1) np.testing.assert_allclose(av1, av2) def test_domain_conversion(self): """Test whether a domain can be used for conversion.""" - data = Orange.data.Table("iris") + data = self.iris interpolated = Interpolate([0.5, 1.5])(data) - nt = Orange.data.Table.from_table(interpolated.domain, data) + nt = data.transform(interpolated.domain) self.assertEqual(interpolated.domain, nt.domain) - np.testing.assert_equal(interpolated.X, nt.X) - np.testing.assert_equal(interpolated.Y, nt.Y) + np.testing.assert_equal(np.asarray(interpolated.X), np.asarray(nt.X)) + np.testing.assert_equal(np.asarray(interpolated.Y), np.asarray(nt.Y)) def test_same(self): """Interpolate values are original values.""" - data = Orange.data.Table("iris") + data = self.iris interpolated = Interpolate(range(len(data.domain.attributes)))(data) np.testing.assert_allclose(interpolated.X, data.X) def test_permute(self): rs = np.random.RandomState(0) - data = Orange.data.Table("iris") + data = self.iris oldX = data.X #permute data p = rs.permutation(range(len(data.domain.attributes))) @@ -61,13 +83,13 @@ def test_permute(self): np.testing.assert_allclose(interpolated.X, oldX[:, p1]) def test_out_of_band(self): - data = Orange.data.Table("iris") + data = self.iris interpolated = Interpolate(range(-1, len(data.domain.attributes)+1))(data) np.testing.assert_allclose(interpolated.X[:, 1:5], data.X) - np.testing.assert_equal(interpolated.X[:, [0, -1]], np.nan) + np.testing.assert_equal(np.asarray(interpolated.X[:, [0, -1]]), np.nan) def test_unknown_middle(self): - data = Orange.data.Table("iris") + data = self.iris.copy() # whole column in the middle should be interpolated with data.unlocked(): data.X[:, 1] = np.nan @@ -75,7 +97,7 @@ def test_unknown_middle(self): self.assertFalse(np.any(np.isnan(interpolated.X))) def test_unknown_elsewhere(self): - data = Orange.data.Table("iris") + data = self.iris.copy() with data.unlocked(): data.X[0, 1] = np.nan data.X[1, 1] = np.nan @@ -88,7 +110,7 @@ def test_unknown_elsewhere(self): self.assertFalse(np.any(np.isnan(interpolated.X))) def test_unknown_elsewhere_different(self): - data = Orange.data.Table("iris") + data = self.iris.copy() with data.unlocked(): data.X[0, 1] = np.nan data.X[1, 1] = np.nan @@ -96,12 +118,14 @@ def test_unknown_elsewhere_different(self): im = Interpolate(getx(data)) im.interpfn = interp1d_with_unknowns_numpy interpolated = im(data) + self.assertIsInstance(interpolated.X, type(data.X)) self.assertAlmostEqual(interpolated.X[0, 1], 3.25) self.assertAlmostEqual(interpolated.X[1, 1], 3.333333333333334) self.assertAlmostEqual(interpolated.X[1, 2], 1.766666666666667) self.assertFalse(np.any(np.isnan(interpolated.X))) im.interpfn = interp1d_with_unknowns_scipy interpolated = im(data) + self.assertIsInstance(interpolated.X, type(data.X)) self.assertAlmostEqual(interpolated.X[0, 1], 3.25) self.assertAlmostEqual(interpolated.X[1, 1], 3.333333333333334) self.assertAlmostEqual(interpolated.X[1, 2], 1.766666666666667) @@ -109,16 +133,65 @@ def test_unknown_elsewhere_different(self): save_X = interpolated.X im.interpfn = interp1d_wo_unknowns_scipy interpolated = im(data) + self.assertIsInstance(interpolated.X, type(data.X)) self.assertTrue(np.any(np.isnan(interpolated.X))) # parts without unknown should be the same - np.testing.assert_almost_equal(data.X[2:], save_X[2:]) + np.testing.assert_allclose(data.X[2:], save_X[2:]) + + def test_nan_extend_edges_and_interpolate(self): + data = self.iris.copy() + with data.unlocked(): + data.X[0, :] = np.nan + data.X[1, 1] = np.nan + data.X[2, 0] = np.nan + data.X[3, -1] = np.nan + xs = getx(data) + interp, unknowns = nan_extend_edges_and_interpolate(xs, data.X) + self.assertIsInstance(interp, type(data.X)) + nan = float("nan") + res = np.array([[nan, nan, nan, nan], + [4.9, 3.15, 1.4, 0.2], + [3.2, 3.2, 1.3, 0.2], + [4.6, 3.1, 1.5, 1.5], + [5., 3.6, 1.4, 0.2]]) + resu = np.array([[True, True, True, True], + [False, True, False, False], + [True, False, False, False], + [False, False, False, True], + [False, False, False, False]]) + np.testing.assert_allclose(interp, res) + np.testing.assert_allclose(unknowns, resu) + + def test_nan_extend_edges_and_interpolate_mixed(self): + data = self.range16 + + xs = getx(data) + ys = data.X + v, n = nan_extend_edges_and_interpolate(xs, ys) + exp = np.arange(16, dtype=float).reshape(4, 4) + exp[0, 0] = 1 + exp[3, 3] = 14 + np.testing.assert_equal(v, exp) + + mix = np.array([0, 2, 1, 3]) + xsm = xs[mix] + ysm = ys[:, mix] + v, n = nan_extend_edges_and_interpolate(xsm, ysm) + np.testing.assert_equal(v[:, mix], exp) class TestInterpolateToDomain(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = Orange.data.Table("iris") + cls.housing = Orange.data.Table("housing") + cls.titanic = Orange.data.Table("titanic") + def test_same_domain(self): - iris = Orange.data.Table("iris") - housing = Orange.data.Table("housing") + iris = self.iris + housing = self.housing iiris = InterpolateToDomain(target=housing)(iris) self.assertNotEqual(housing.domain.attributes, iris.domain.attributes) # needs to have the same attributes @@ -128,8 +201,28 @@ def test_same_domain(self): np.testing.assert_equal(np.isnan(iiris.X[0])[4:], True) def test_not_all_continuous(self): - titanic = Orange.data.Table("titanic") - iris = Orange.data.Table("iris") + titanic = self.titanic + iris = self.iris InterpolateToDomain(target=iris) with self.assertRaises(NotAllContinuousException): InterpolateToDomain(target=titanic) + + +@unittest.skipUnless(dask, "installed Orange does not support dask") +class TestInterpolateDask(TestInterpolate): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = temp_dasktable(cls.iris) + cls.collagen = temp_dasktable(cls.collagen) + cls.range16 = temp_dasktable(cls.range16) + + +@unittest.skipUnless(dask, "installed Orange does not support dask") +class TestInterpolateToDomainDask(TestInterpolateToDomain): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = temp_dasktable(cls.iris) + cls.housing = temp_dasktable(cls.housing) + cls.titanic = temp_dasktable(cls.titanic) diff --git a/orangecontrib/spectroscopy/tests/test_owhyper.py b/orangecontrib/spectroscopy/tests/test_owhyper.py index 3a01899fc..1ca23cb76 100644 --- a/orangecontrib/spectroscopy/tests/test_owhyper.py +++ b/orangecontrib/spectroscopy/tests/test_owhyper.py @@ -7,6 +7,12 @@ import numpy as np from PIL import Image +try: + import dask + from Orange.tests.test_dasktable import temp_dasktable +except ImportError: + dask = None + from AnyQt.QtCore import QPointF, Qt, QRectF from AnyQt.QtTest import QSignalSpy import Orange @@ -113,11 +119,11 @@ def setUpClass(cls): # dataset without rows empty = cls.iris[:0] # dataset with large blank regions - irisunknown = Interpolate(np.arange(20))(cls.iris) + irisunknown = Interpolate(np.arange(20))(cls.iris)[:20] # dataset without any attributes, but XY whitelight0 = cls.whitelight.transform( Orange.data.Domain([], None, metas=cls.whitelight.domain.metas))[:100] - unknowns = cls.iris.copy() + unknowns = cls.iris[::10].copy() with unknowns.unlocked(): unknowns.X[:, :] = float("nan") single_pixel = cls.iris[:50] # all image coordinates map to one spot @@ -195,9 +201,11 @@ def test_simple(self): self.assertIsNone(self.get_output("Selection"), None) def test_unknown(self): - self.send_signal("Data", self.whitelight) + self.send_signal("Data", self.whitelight[:10]) + wait_for_image(self.widget) levels = self.widget.imageplot.img.levels - self.send_signal("Data", self.whitelight_unknown) + self.send_signal("Data", self.whitelight_unknown[:10]) + wait_for_image(self.widget) levelsu = self.widget.imageplot.img.levels np.testing.assert_equal(levelsu, levels) @@ -265,7 +273,8 @@ def test_select_click_multiple_groups(self): oldvars = data.domain.variables + data.domain.metas group_at = [a for a in newvars if a not in oldvars][0] unselected = group_at.to_val("Unselected") - out = out[np.flatnonzero(out.transform(Orange.data.Domain([group_at])).X != unselected)] + out = out[np.asarray(np.flatnonzero( + out.transform(Orange.data.Domain([group_at])).X != unselected))] self.assertEqual(len(out), 4) np.testing.assert_equal([o["map_x"].value for o in out], [1, 2, 3, 4]) np.testing.assert_equal([o[group_at].value for o in out], ["G1", "G2", "G3", "G3"]) @@ -294,7 +303,7 @@ def test_settings_curves(self): self.assertEqual(self.widget.curveplot.feature_color.name, "iris") def test_set_variable_color(self): - data = Orange.data.Table("iris.tab") + data = self.iris ndom = Orange.data.Domain(data.domain.attributes[:-1], data.domain.class_var, metas=[data.domain.attributes[-1]]) data = data.transform(ndom) @@ -369,7 +378,7 @@ def test_save_graph(self): self.assertGreater(os.path.getsize(fname), 1000) def test_unknown_values_axes(self): - data = Orange.data.Table("iris") + data = self.iris.copy() with data.unlocked(): data.Y[0] = np.nan self.send_signal("Data", data) @@ -472,6 +481,20 @@ def test_compat_no_group(self): self.assertTrue(self.widget.compat_no_group) + +@unittest.skipUnless(dask, "installed Orange does not support dask") +class TestOWHyperWithDask(TestOWHyper): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = temp_dasktable("iris") + cls.whitelight = temp_dasktable("whitelight.gsf") + cls.whitelight_unknown = temp_dasktable(cls.whitelight_unknown) + cls.iris1 = temp_dasktable(cls.iris1) + cls.strange_data = [temp_dasktable(d) if d is not None else None + for d in cls.strange_data] + + class TestVisibleImage(WidgetTest): @classmethod diff --git a/orangecontrib/spectroscopy/utils/__init__.py b/orangecontrib/spectroscopy/utils/__init__.py index b5ea1f61b..9836e8a8f 100644 --- a/orangecontrib/spectroscopy/utils/__init__.py +++ b/orangecontrib/spectroscopy/utils/__init__.py @@ -20,11 +20,11 @@ def apply_columns_numpy(array, function, selector=None, chunk_size=10 ** 7, call def values_to_linspace(vals): - """Find a near maching linspace for the values given. + """Find a near matching linspace for the values given. The problem is that some values can be missing and that they are inexact. The minumum and maximum values are kept as limits.""" - vals = vals[~np.isnan(vals)] + vals = np.asarray(vals[~np.isnan(vals)]) if len(vals): vals = np.unique(vals) # returns sorted array if len(vals) == 1: diff --git a/orangecontrib/spectroscopy/widgets/owhyper.py b/orangecontrib/spectroscopy/widgets/owhyper.py index 9d04cc59b..6118e6be1 100644 --- a/orangecontrib/spectroscopy/widgets/owhyper.py +++ b/orangecontrib/spectroscopy/widgets/owhyper.py @@ -86,6 +86,7 @@ def add_marking(a): if el[0] == "curve": bs_x, bs_ys, penargs = el[1] + bs_x, bs_ys = np.asarray(bs_x), np.asarray(bs_ys) curve = pg.PlotCurveItem() curve.setPen(pg.mkPen(color=QColor(color), **penargs)) curve.setZValue(10) @@ -94,6 +95,7 @@ def add_marking(a): elif el[0] == "fill": (x1, ys1), (x2, ys2) = el[1] + bs_x, bs_ys = np.asarray(bs_x), np.asarray(bs_ys) phigh = pg.PlotCurveItem(x1, ys1[0], pen=None) plow = pg.PlotCurveItem(x2, ys2[0], pen=None) color = QColor(color) @@ -933,41 +935,43 @@ def extract_col(data, var): and lsx[-1] * lsy[-1] > IMAGE_TOO_BIG: raise ImageTooBigException((lsx[-1], lsy[-1])) + ims = image_values(data[:1]).X + d = np.full((data.X.shape[0], ims.shape[1]), float("nan")) + res.d = d + + step = 100000 if len(data) > 1e6 else 10000 + if lsx is not None and lsy is not None: # the code below does this, but part-wise: # d = image_values(data).X[:, 0] - parts = [] - for slice in split_to_size(len(data), 10000): + for slice in split_to_size(len(data), step): part = image_values(data[slice]).X - parts.append(part) + d[slice, :] = part progress_interrupt(0) - d = np.concatenate(parts) - else: - # no need to compute integrals when nothing will be shown - d = np.full((data.X.shape[0], 1), float("nan")) + state.set_partial_result(res) - res.d = d progress_interrupt(0) return res - def on_done(self, res): - - self.lsx, self.lsy = res.lsx, res.lsy - lsx, lsy = self.lsx, self.lsy - + def draw(self, res, finished=False): d = res.d + lsx, lsy = res.lsx, res.lsy self.fixed_levels = res.image_values_fixed_levels + if finished: + self.lsx, self.lsy = lsx, lsy + self.data_points = res.data_points - self.data_points = res.data_points - - xindex, xnan = index_values_nan(res.coorx, self.lsx) - yindex, ynan = index_values_nan(res.coory, self.lsy) - self.data_valid_positions = valid = np.logical_not(np.logical_or(xnan, ynan)) + xindex, xnan = index_values_nan(res.coorx, lsx) + yindex, ynan = index_values_nan(res.coory, lsy) + valid = np.logical_not(np.logical_or(xnan, ynan)) invalid_positions = len(d) - np.sum(valid) - if invalid_positions: - self.parent.Information.not_shown(invalid_positions) + + if finished: + self.data_valid_positions = valid + if invalid_positions: + self.parent.Information.not_shown(invalid_positions) if lsx is not None and lsy is not None: imdata = np.ones((lsy[2], lsx[2], d.shape[1])) * float("nan") @@ -990,11 +994,15 @@ def on_done(self, res): height = (lsy[1]-lsy[0]) + 2*shifty self.img.setRect(QRectF(left, bottom, width, height)) - self.refresh_img_selection() - self.image_updated.emit() + if finished: + self.refresh_img_selection() + self.image_updated.emit() - def on_partial_result(self, result): - pass + def on_done(self, res): + self.draw(res, finished=True) + + def on_partial_result(self, res): + self.draw(res) def on_exception(self, ex: Exception): if isinstance(ex, InterruptException): diff --git a/orangecontrib/spectroscopy/widgets/owspectra.py b/orangecontrib/spectroscopy/widgets/owspectra.py index 4558d0bc0..1db4a7a5a 100644 --- a/orangecontrib/spectroscopy/widgets/owspectra.py +++ b/orangecontrib/spectroscopy/widgets/owspectra.py @@ -9,9 +9,6 @@ try: import dask import dask.array as da - import dask.distributed - dask_client = dask.distributed.Client(processes=False, n_workers=2, - threads_per_worker=4, dashboard_address=None) except ImportError: dask = None @@ -46,6 +43,7 @@ from Orange.widgets.visualize.utils.plotutils import HelpEventDelegate, PlotWidget from Orange.widgets.visualize.utils.customizableplot import CommonParameterSetter +from orangecontrib.spectroscopy import dask_client from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.utils import apply_columns_numpy from orangecontrib.spectroscopy.widgets.line_geometry import \ @@ -1324,6 +1322,8 @@ def current_selection(): old_sel_ci = current_selection() + changed = False + if add_to_group: # both keys - need to test it before add_group selnum = np.max(self.selection_group) elif add_group: @@ -1333,15 +1333,20 @@ def current_selection(): else: # remove the current selection redraw_curve_indices.update(old_sel_ci) - self.selection_group *= 0 # remove + if np.any(self.selection_group): + self.selection_group *= 0 # remove + changed = True selnum = 1 # add new if data_indices is not None: self.selection_group[data_indices] = selnum redraw_curve_indices.update( icurve for idata, icurve in invd.items() if idata in data_indices_set) + changed = True fixes = self.make_selection_valid() + if fixes: + changed = True redraw_curve_indices.update( icurve for idata, icurve in invd.items() if idata in fixes) @@ -1353,7 +1358,8 @@ def current_selection(): redraw_curve_indices.update(old_sel_ci) self.set_curve_pens(redraw_curve_indices) - self.selection_changed_confirm() + if changed: + self.selection_changed_confirm() def make_selection_valid(self): """ Make the selection valid and return the changed positions. """