Example #1
0
class TestListModel(GuiTest):
    def setUp(self):
        self.widget = OWWidget()
        self.widget.foo = None
        self.attrs = VariableListModel()
        self.view = gui.listView(
            self.widget.controlArea, self.widget, "foo", model=self.attrs)

    def tearDown(self) -> None:
        self.widget.deleteLater()
        del self.widget

    def test_select_callback(self):
        widget = self.widget
        view = self.view

        self.assertIsNone(widget.foo)

        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]

        view.setCurrentIndex(self.attrs.index(0, 0))
        self.assertIs(widget.foo, a)
        view.setCurrentIndex(self.attrs.index(2, 0))
        self.assertIs(widget.foo, c)

        view.setSelectionMode(view.MultiSelection)
        sel_model = view.selectionModel()
        sel_model.clear()
        view.setCurrentIndex(self.attrs.index(1, 0))
        self.assertEqual(widget.foo, [b])

    def test_select_callfront(self):
        widget = self.widget
        view = self.view

        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]

        widget.foo = b
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 1)
        self.assertEqual(selection[0].row(), 1)

        view.setSelectionMode(view.MultiSelection)
        widget.foo = [a, c]
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 2)
        self.assertEqual({selection[0].row(), selection[1].row()}, {0, 2})

        widget.foo = []
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 0)

        widget.foo = [2, "b"]
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 2)
        self.assertEqual({selection[0].row(), selection[1].row()}, {1, 2})
Example #2
0
class TestListModel(GuiTest):
    def setUp(self):
        self.widget = OWWidget()
        self.widget.foo = None
        self.attrs = VariableListModel()
        self.view = gui.listView(
            self.widget.controlArea, self.widget, "foo", model=self.attrs)

    def test_select_callback(self):
        widget = self.widget
        view = self.view

        self.assertIsNone(widget.foo)

        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]

        view.setCurrentIndex(self.attrs.index(0, 0))
        self.assertIs(widget.foo, a)
        view.setCurrentIndex(self.attrs.index(2, 0))
        self.assertIs(widget.foo, c)

        view.setSelectionMode(view.MultiSelection)
        sel_model = view.selectionModel()
        sel_model.clear()
        view.setCurrentIndex(self.attrs.index(1, 0))
        self.assertEqual(widget.foo, [b])

    def test_select_callfront(self):
        widget = self.widget
        view = self.view

        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]

        widget.foo = b
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 1)
        self.assertEqual(selection[0].row(), 1)

        view.setSelectionMode(view.MultiSelection)
        widget.foo = [a, c]
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 2)
        self.assertEqual({selection[0].row(), selection[1].row()}, {0, 2})

        widget.foo = []
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 0)

        widget.foo = [2, "b"]
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 2)
        self.assertEqual({selection[0].row(), selection[1].row()}, {1, 2})
Example #3
0
class TestListModel(GuiTest):
    def test_select(self):
        widget = OWWidget()
        widget.foo = None
        self.attrs = VariableListModel()
        view = gui.listView(widget.controlArea, widget, "foo", model=self.attrs)
        self.assertIsNone(widget.foo)
        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]
        view.setCurrentIndex(self.attrs.index(0, 0))
        self.assertIs(widget.foo, a)
        view.setCurrentIndex(self.attrs.index(2, 0))
        self.assertIs(widget.foo, c)

        widget.foo = b
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 1)
        self.assertEqual(selection[0].row(), 1)
Example #4
0
class TestListModel(GuiTest):
    def test_select(self):
        widget = OWWidget()
        widget.foo = None
        self.attrs = VariableListModel()
        view = gui.listView(widget.controlArea, widget, "foo", model=self.attrs)
        self.assertIsNone(widget.foo)
        a, b, c = (ContinuousVariable(x) for x in "abc")
        self.attrs[:] = [a, b, c]
        view.setCurrentIndex(self.attrs.index(0, 0))
        self.assertIs(widget.foo, a)
        view.setCurrentIndex(self.attrs.index(2, 0))
        self.assertIs(widget.foo, c)

        widget.foo = b
        selection = view.selectedIndexes()
        self.assertEqual(len(selection), 1)
        self.assertEqual(selection[0].row(), 1)
Example #5
0
 def test_delegate(self):
     cases = (
         (DState(Default(Leave()), None, None), ""),
         (DState(Leave(), None, None), "(leave)"),
         (DState(MDL(), [1], None), "(entropy)"),
         (DState(MDL(), [], None), "<removed>"),
         (DState(EqualFreq(2), [1], None), "(equal frequency k=2)"),
         (DState(EqualWidth(2), [1], None), "(equal width k=2)"),
         (DState(Remove(), None, None), "(removed)"),
         (DState(Custom([1]), None, None), "(custom)"),
     )
     delegate = DiscDelegate()
     var = DiscreteVariable("C", ("a", "b"))
     model = VariableListModel()
     model.append(var)
     for state, text in cases:
         model.setData(model.index(0), state, Qt.UserRole)
         option = QStyleOptionViewItem()
         delegate.initStyleOption(option, model.index(0))
         self.assertIn(text, option.text)
class OWSpiralogram(widget.OWWidget):
    name = 'Spiralogram'
    description = "Visualize time series' periodicity in a spiral heatmap."
    icon = 'icons/Spiralogram.svg'
    priority = 120

    class Inputs:
        time_series = Input("Time series", Table)

    class Outputs:
        time_series = Output("Time series", Timeseries)

    settingsHandler = settings.DomainContextHandler()

    ax1 = settings.ContextSetting('months of year')
    ax2 = settings.ContextSetting('years')

    agg_attr = settings.ContextSetting([])
    agg_func = settings.ContextSetting(0)

    invert_date_order = settings.Setting(False)

    graph_name = 'chart'

    class Error(widget.OWWidget.Error):
        no_time_variable = widget.Msg(
            'Spiralogram requires time series with a time variable.')

    def __init__(self):
        self.data = None
        self.indices = []
        box = gui.vBox(self.controlArea, 'Axes')
        self.combo_ax2_model = VariableListModel(parent=self)
        self.combo_ax1_model = VariableListModel(parent=self)
        for model in (self.combo_ax1_model, self.combo_ax2_model):
            model[:] = [_enum_str(i) for i in Spiralogram.AxesCategories]
        self.combo_ax2 = gui.comboBox(box,
                                      self,
                                      'ax2',
                                      label='Y axis:',
                                      callback=self.replot,
                                      sendSelectedValue=True,
                                      orientation='horizontal',
                                      model=self.combo_ax2_model)
        self.combo_ax1 = gui.comboBox(box,
                                      self,
                                      'ax1',
                                      label='Radial:',
                                      callback=self.replot,
                                      sendSelectedValue=True,
                                      orientation='horizontal',
                                      model=self.combo_ax1_model)
        gui.checkBox(box,
                     self,
                     'invert_date_order',
                     'Invert Y axis order',
                     callback=self.replot)

        box = gui.vBox(self.controlArea, 'Aggregation')
        self.combo_func = gui.comboBox(box,
                                       self,
                                       'agg_func',
                                       label='Function:',
                                       orientation='horizontal',
                                       callback=self.replot)
        func_model = ListModel(AGG_FUNCTIONS, parent=self)
        self.combo_func.setModel(func_model)

        self.attrlist_model = VariableListModel(parent=self)
        self.attrlist = QListView(selectionMode=QListView.SingleSelection)
        self.attrlist.setModel(self.attrlist_model)
        self.attrlist.selectionModel().selectionChanged.connect(
            self.attrlist_selectionChanged)
        box.layout().addWidget(self.attrlist)

        gui.rubber(self.controlArea)

        self.chart = chart = Spiralogram(self,
                                         selection_callback=self.on_selection)
        self.mainArea.layout().addWidget(chart)

    def attrlist_selectionChanged(self):
        self.agg_attr = [
            self.attrlist_model[i.row()]
            for i in self.attrlist.selectionModel().selectedIndexes()
        ]
        self.replot()

    @Inputs.time_series
    def set_data(self, data):
        self.Error.clear()
        self.data = data = None if data is None else Timeseries.from_data_table(
            data)

        if data is None:
            self.commit()
            return

        if self.data.time_variable is None or not isinstance(
                self.data.time_variable, TimeVariable):
            self.Error.no_time_variable()
            self.commit()
            return

        def init_combos():
            for model in (self.combo_ax1_model, self.combo_ax2_model):
                model.clear()
            newmodel = []
            if data is not None and data.time_variable is not None:
                for model in (self.combo_ax1_model, self.combo_ax2_model):
                    model[:] = [
                        _enum_str(i) for i in Spiralogram.AxesCategories
                    ]
            for var in data.domain.variables if data is not None else []:
                if (var.is_primitive()
                        and (var is not data.time_variable or isinstance(
                            var, TimeVariable) and data.time_delta is None)):
                    newmodel.append(var)
                if var.is_discrete:
                    for model in (self.combo_ax1_model, self.combo_ax2_model):
                        model.append(var)
            self.attrlist_model.wrap(newmodel)

        init_combos()
        self.chart.clear()

        self.closeContext()
        self.ax2 = next((self.combo_ax2.itemText(i)
                         for i in range(self.combo_ax2.count())), '')
        self.ax1 = next((self.combo_ax1.itemText(i)
                         for i in range(1, self.combo_ax1.count())), self.ax2)
        self.agg_attr = [data.domain.variables[0]] if len(
            data.domain.variables) else []
        self.agg_func = 0
        if getattr(data, 'time_variable', None) is not None:
            self.openContext(data.domain)

        if self.agg_attr:
            self.attrlist.blockSignals(True)
            self.attrlist.selectionModel().clear()
            for attr in self.agg_attr:
                try:
                    row = self.attrlist_model.indexOf(attr)
                except ValueError:
                    continue
                self.attrlist.selectionModel().select(
                    self.attrlist_model.index(row),
                    QItemSelectionModel.SelectCurrent)
            self.attrlist.blockSignals(False)

        self.replot()

    def replot(self):
        if not self.combo_ax1.count() or not self.agg_attr:
            return self.chart.clear()

        vars = self.agg_attr
        func = AGG_FUNCTIONS[self.agg_func]
        if any(var.is_discrete for var in vars) and func != Mode:
            self.combo_func.setCurrentIndex(AGG_FUNCTIONS.index(Mode))
            func = Mode
        try:
            ax1 = Spiralogram.AxesCategories[_enum_str(self.ax1, True)]
        except KeyError:
            ax1 = self.data.domain[self.ax1]
        # TODO: Allow having only a single (i.e. radial) axis
        try:
            ax2 = Spiralogram.AxesCategories[_enum_str(self.ax2, True)]
        except KeyError:
            ax2 = self.data.domain[self.ax2]
        self.chart.setSeries(self.data, vars, ax1, ax2, func)

    def on_selection(self, indices):
        self.indices = self.chart.selection_indices(indices)
        self.commit()

    def commit(self):
        self.Outputs.time_series.send(
            self.data[self.indices] if self.data else None)
class OWSpiralogram(widget.OWWidget):
    name = 'Spiralogram'
    description = "Visualize time series' periodicity in a spiral heatmap."
    icon = 'icons/Spiralogram.svg'
    priority = 120

    class Inputs:
        time_series = Input("Time series", Table)

    class Outputs:
        time_series = Output("Time series", Timeseries)

    settingsHandler = settings.DomainContextHandler()

    ax1 = settings.ContextSetting('months of year')
    ax2 = settings.ContextSetting('years')

    agg_attr = settings.ContextSetting([])
    agg_func = settings.ContextSetting(0)

    invert_date_order = settings.Setting(False)

    graph_name = 'chart'

    def __init__(self):
        self.data = None
        self.indices = []
        box = gui.vBox(self.controlArea, 'Axes')
        self.combo_ax2_model = VariableListModel(parent=self)
        self.combo_ax1_model = VariableListModel(parent=self)
        for model in (self.combo_ax1_model, self.combo_ax2_model):
            model[:] = [_enum_str(i) for i in Spiralogram.AxesCategories]
        self.combo_ax2 = gui.comboBox(
            box, self, 'ax2', label='Y axis:', callback=self.replot,
            sendSelectedValue=True, orientation='horizontal',
            model=self.combo_ax2_model)
        self.combo_ax1 = gui.comboBox(
            box, self, 'ax1', label='Radial:', callback=self.replot,
            sendSelectedValue=True, orientation='horizontal',
            model=self.combo_ax1_model)
        gui.checkBox(box, self, 'invert_date_order', 'Invert Y axis order',
                     callback=self.replot)

        box = gui.vBox(self.controlArea, 'Aggregation')
        self.combo_func = gui.comboBox(
            box, self, 'agg_func', label='Function:', orientation='horizontal',
            callback=self.replot)
        func_model = ListModel(AGG_FUNCTIONS, parent=self)
        self.combo_func.setModel(func_model)

        self.attrlist_model = VariableListModel(parent=self)
        self.attrlist = QListView(selectionMode=QListView.SingleSelection)
        self.attrlist.setModel(self.attrlist_model)
        self.attrlist.selectionModel().selectionChanged.connect(
            self.attrlist_selectionChanged)
        box.layout().addWidget(self.attrlist)

        gui.rubber(self.controlArea)

        self.chart = chart = Spiralogram(self,
                                         selection_callback=self.on_selection)
        self.mainArea.layout().addWidget(chart)

    def attrlist_selectionChanged(self):
        self.agg_attr = [self.attrlist_model[i.row()]
                         for i in self.attrlist.selectionModel().selectedIndexes()]
        self.replot()

    @Inputs.time_series
    def set_data(self, data):
        self.data = data = None if data is None else Timeseries.from_data_table(data)

        def init_combos():
            for model in (self.combo_ax1_model, self.combo_ax2_model):
                model.clear()
            newmodel = []
            if data is not None and data.time_variable is not None:
                for model in (self.combo_ax1_model, self.combo_ax2_model):
                    model[:] = [_enum_str(i) for i in Spiralogram.AxesCategories]
            for var in data.domain.variables if data is not None else []:
                if (var.is_primitive() and
                        (var is not data.time_variable or
                         isinstance(var, TimeVariable) and data.time_delta is None)):
                    newmodel.append(var)
                if var.is_discrete:
                    for model in (self.combo_ax1_model, self.combo_ax2_model):
                        model.append(var)
            self.attrlist_model.wrap(newmodel)

        init_combos()
        self.chart.clear()

        if data is None:
            self.commit()
            return

        self.closeContext()
        self.ax2 = next((self.combo_ax2.itemText(i)
                         for i in range(self.combo_ax2.count())), '')
        self.ax1 = next((self.combo_ax1.itemText(i)
                         for i in range(1, self.combo_ax1.count())), self.ax2)
        self.agg_attr = [data.domain.variables[0]] if len(data.domain.variables) else []
        self.agg_func = 0
        if getattr(data, 'time_variable', None) is not None:
            self.openContext(data.domain)

        if self.agg_attr:
            self.attrlist.blockSignals(True)
            self.attrlist.selectionModel().clear()
            for attr in self.agg_attr:
                try:
                    row = self.attrlist_model.indexOf(attr)
                except ValueError:
                    continue
                self.attrlist.selectionModel().select(
                    self.attrlist_model.index(row),
                    QItemSelectionModel.SelectCurrent)
            self.attrlist.blockSignals(False)

        self.replot()

    def replot(self):
        if not self.combo_ax1.count() or not self.agg_attr:
            return self.chart.clear()

        vars = self.agg_attr
        func = AGG_FUNCTIONS[self.agg_func]
        if any(var.is_discrete for var in vars) and func != Mode:
            self.combo_func.setCurrentIndex(AGG_FUNCTIONS.index(Mode))
            func = Mode
        try:
            ax1 = Spiralogram.AxesCategories[_enum_str(self.ax1, True)]
        except KeyError:
            ax1 = self.data.domain[self.ax1]
        # TODO: Allow having only a sinle (i.e. radial) axis
        try:
            ax2 = Spiralogram.AxesCategories[_enum_str(self.ax2, True)]
        except KeyError:
            ax2 = self.data.domain[self.ax2]
        self.chart.setSeries(self.data, vars, ax1, ax2, func)

    def on_selection(self, indices):
        self.indices = self.chart.selection_indices(indices)
        self.commit()

    def commit(self):
        self.Outputs.time_series.send(self.data[self.indices] if self.data else None)