diff --git a/Orange/widgets/visualize/owdistributions.py b/Orange/widgets/visualize/owdistributions.py index 00cadc7bf5c..1380cad706d 100644 --- a/Orange/widgets/visualize/owdistributions.py +++ b/Orange/widgets/visualize/owdistributions.py @@ -65,7 +65,7 @@ def mouseReleaseEvent(event): class DistributionBarItem(pg.GraphicsObject): def __init__(self, x, width, padding, freqs, colors, stacked, expanded, - tooltip): + tooltip, hidden): super().__init__() self.x = x self.width = width @@ -77,8 +77,10 @@ def __init__(self, x, width, padding, freqs, colors, stacked, expanded, self.__picture = None self.polygon = None self.hovered = False + self._tooltip = tooltip + self.hidden = False + self.setHidden(hidden) self.setAcceptHoverEvents(True) - self.setToolTip(tooltip) def hoverEnterEvent(self, event): super().hoverEnterEvent(event) @@ -90,7 +92,15 @@ def hoverLeaveEvent(self, event): self.hovered = False self.update() + def setHidden(self, hidden): + self.hidden = hidden + if not hidden: + self.setToolTip(self._tooltip) + def paint(self, painter, _options, _widget): + if self.hidden: + return + if self.expanded: tot = np.sum(self.freqs) if tot == 0: @@ -264,6 +274,7 @@ class Warning(OWWidget.Warning): number_of_bins = settings.ContextSetting(5, schema_only=True) fitted_distribution = settings.Setting(0) + hide_bars = settings.Setting(False) show_probs = settings.Setting(False) stacked_columns = settings.Setting(False) cumulative_distr = settings.Setting(False) @@ -292,6 +303,7 @@ def __init__(self): self.data = None self.valid_data = self.valid_group_data = None self.bar_items = [] + self.curve_items = [] self.curve_descriptions = None self.binnings = [] @@ -326,8 +338,9 @@ def __init__(self): label="Smoothing", orientation=Qt.Horizontal, minValue=2, maxValue=20, callback=self.replot) gui.checkBox( - box, self, "cumulative_distr", "Show cumulative distribution", - callback=self.replot) + box, self, "hide_bars", "Hide bars", stateWhenDisabled=False, + callback=self._on_hide_bars_changed, + disabled=not self.fitted_distribution) box = gui.vBox(self.controlArea, "Columns") gui.comboBox( @@ -341,6 +354,9 @@ def __init__(self): gui.checkBox( box, self, "show_probs", "Show probabilities", callback=self._on_show_probabilities_changed) + gui.checkBox( + box, self, "cumulative_distr", "Show cumulative distribution", + callback=self.replot) gui.auto_apply(self.controlArea, self, commit=self.apply) @@ -385,7 +401,7 @@ def add_new_plot(zvalue): def _setup_legend(self): self._legend = LegendItem() - self._legend.setParentItem(self.plot) + self._legend.setParentItem(self.plot_pdf) self._legend.hide() self._legend.anchor((1, 0), (1, 0)) @@ -449,9 +465,16 @@ def _on_bin_slider_released(self): self.apply() def _on_fitted_dist_changed(self): + self.controls.hide_bars.setDisabled(not self.fitted_distribution) self._set_smoothing_visibility() self.replot() + def _on_hide_bars_changed(self): + for bar in self.bar_items: # pylint: disable=blacklisted-name + bar.setHidden(self.hide_bars) + self._set_curve_brushes() + self.plot.update() + def _set_smoothing_visibility(self): self.smoothing_box.setVisible( self.Fitters[self.fitted_distribution][1] is AshCurve) @@ -525,6 +548,7 @@ def _clear_plot(self): self.plot_pdf.clear() self.plot_mark.clear() self.bar_items = [] + self.curve_items = [] self._legend.clear() self._legend.hide() @@ -564,9 +588,10 @@ def _call_plotting(self): self.plot.autoRange() def _add_bar(self, x, width, padding, freqs, colors, stacked, expanded, - tooltip): + tooltip, hidden=False): item = DistributionBarItem( - x, width, padding, freqs, colors, stacked, expanded, tooltip) + x, width, padding, freqs, colors, stacked, expanded, tooltip, + hidden) self.plot.addItem(item) self.bar_items.append(item) @@ -618,7 +643,8 @@ def _cont_plot(self): f"{freq} ({100 * freq / total:.2f} %)

" self._add_bar( x0, x1 - x0, 0, [tot_freq if self.cumulative_distr else freq], - colors, stacked=False, expanded=False, tooltip=tooltip) + colors, stacked=False, expanded=False, tooltip=tooltip, + hidden=self.hide_bars) if self.fitted_distribution: self._plot_approximations( @@ -657,6 +683,7 @@ def _cont_split_plot(self): self._add_bar( x0, x1 - x0, 0 if self.stacked_columns else 0.1, plotfreqs, gcolors, stacked=self.stacked_columns, expanded=self.show_probs, + hidden=self.hide_bars, tooltip=self._split_tooltip( self.str_int(x0, x1, not i, i == lasti), np.sum(plotfreqs), total, gvalues, plotfreqs)) @@ -734,12 +761,24 @@ def _plot_approximations(self, x0, x1, fitters, colors, prior_probs): tot = (y_p + (tots - y) * (1 - prior_prob)) tot[tot == 0] = 1 y = y_p / tot - plot.addItem(pg.PlotCurveItem( - x=x, y=y, + curve = pg.PlotCurveItem( + x=x, y=y, fillLevel=0, pen=pg.mkPen(width=5, color=color), - shadowPen=pg.mkPen(width=8, color=color.darker(120)))) + shadowPen=pg.mkPen(width=8, color=color.darker(120))) + plot.addItem(curve) + self.curve_items.append(curve) if not show_probs: self.plot_pdf.autoRange() + self._set_curve_brushes() + + def _set_curve_brushes(self): + for curve in self.curve_items: + if self.hide_bars: + color = curve.opts['pen'].color().lighter(160) + color.setAlpha(128) + curve.setBrush(pg.mkBrush(color)) + else: + curve.setBrush(None) @staticmethod def _split_tooltip(valname, tot_group, total, gvalues, freqs): diff --git a/Orange/widgets/visualize/tests/test_owdistributions.py b/Orange/widgets/visualize/tests/test_owdistributions.py index c8b23ce5eb5..5c6af9724aa 100644 --- a/Orange/widgets/visualize/tests/test_owdistributions.py +++ b/Orange/widgets/visualize/tests/test_owdistributions.py @@ -5,7 +5,7 @@ from unittest.mock import Mock import numpy as np -from AnyQt.QtCore import QItemSelection +from AnyQt.QtCore import QItemSelection, Qt from Orange.data import Table, Domain, DiscreteVariable from Orange.widgets.tests.base import WidgetTest @@ -461,6 +461,52 @@ def test_selection_grouping(self): self.assertEqual( len(out_selected.domain[ANNOTATED_DATA_FEATURE_NAME].values), 3) + def test_disable_hide_bars(self): + widget = self.widget + self.send_signal(widget.Inputs.data, self.iris) + domain = self.iris.domain + + cb = widget.controls.hide_bars + + for var in ("petal length", "iris"): + for fitter in (0, 1): + self._set_var(domain[var]) + self._set_fitter(fitter) + self.assertEqual(cb.isEnabled(), + var == "petal length" and fitter == 1) + + def test_hide_bars(self): + # bar is a valid name in this context, pylint: disable=blacklisted-name + widget = self.widget + cb = widget.controls.hide_bars + self._set_check(cb, True) + self._set_fitter(1) + + self.send_signal(widget.Inputs.data, self.iris) + + domain = self.iris.domain + self._set_var(domain["petal length"]) + + for cvar in (None, domain["iris"]): + self._set_cvar(cvar) + self.assertTrue(cb.isEnabled()) + self.assertTrue(all(bar.hidden + for bar in widget.bar_items)) + self.assertTrue(all(curve.opts["brush"] is not None + for curve in widget.curve_items)) + + self._set_check(cb, False) + self.assertTrue(all(not bar.hidden + for bar in widget.bar_items)) + self.assertTrue(all(curve.opts["brush"].style() == Qt.NoBrush + for curve in widget.curve_items)) + + self._set_check(cb, True) + self.assertTrue(all(bar.hidden + for bar in widget.bar_items)) + self.assertTrue(all(curve.opts["brush"] is not None + for curve in widget.curve_items)) + def test_report(self): """Report doesn't crash""" widget = self.widget