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})
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})
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)
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)
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)