def test_variable_model(self): model = VariableListModel() self.assertEqual(model.effective_name(model.index(-1, -1)), None) def data(row, role): return model.data(model.index(row, ), role) def set_data(row, data, role): model.setData(model.index(row), data, role) model[:] = [ RealVector(Real("A", (3, "g"), (), False), lambda: MArray([])), RealVector(Real("B", (3, "g"), (), False), lambda: MArray([])), ] self.assertEqual(data(0, Qt.DisplayRole), "A") self.assertEqual(data(1, Qt.DisplayRole), "B") self.assertEqual(model.effective_name(model.index(1)), "B") set_data(1, [Rename("A")], TransformRole) self.assertEqual(model.effective_name(model.index(1)), "A") self.assertEqual(data(0, MultiplicityRole), 2) self.assertEqual(data(1, MultiplicityRole), 2) set_data(1, [], TransformRole) self.assertEqual(data(0, MultiplicityRole), 1) self.assertEqual(data(1, MultiplicityRole), 1)
class TestEditors(GuiTest): def test_variable_editor(self): w = VariableEditor() self.assertEqual(w.get_data(), (None, [])) v = String("S", (("A", "1"), ("B", "b"))) w.set_data(v, []) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), {"A": "1", "B": "b"}) self.assertEqual(w.get_data(), (v, [])) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) w.set_data(v, [Rename("T"), Annotate((("a", "1"), ("b", "2")))]) self.assertEqual(w.name_edit.text(), "T") self.assertEqual(w.labels_model.rowCount(), 2) add = w.findChild(QAction, "action-add-label") add.trigger() remove = w.findChild(QAction, "action-delete-label") remove.trigger() def test_continuous_editor(self): w = ContinuousVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Real("X", (-1, ""), (("A", "1"), ("B", "b"))) w.set_data(v, []) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) def test_discrete_editor(self): w = DiscreteVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b"))) w.set_data(v) self.assertEqual(w.name_edit.text(), v.name) self.assertFalse(w.ordered_cb.isChecked()) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) self.assertEqual(w.get_data(), (v, [])) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) mapping = [("c", "C"), ("a", "A"), ("b", None), (None, "b")] w.set_data(v, [CategoriesMapping(mapping)]) w.grab() # run delegate paint method self.assertEqual(w.get_data(), (v, [CategoriesMapping(mapping)])) w.set_data(v, [CategoriesMapping(mapping), ChangeOrdered(True)]) self.assertTrue(w.ordered_cb.isChecked()) self.assertEqual(w.get_data()[1], [CategoriesMapping(mapping), ChangeOrdered(True)]) # test selection/deselection in the view w.set_data(v) view = w.values_edit model = view.model() assert model.rowCount() sel_model = view.selectionModel() model = sel_model.model() sel_model.select(model.index(0, 0), QItemSelectionModel.Select) sel_model.select(model.index(0, 0), QItemSelectionModel.Deselect) # merge mapping mapping = [("a", "a"), ("b", "b"), ("c", "b")] w.set_data(v, [CategoriesMapping(mapping)]) self.assertEqual(w.get_data()[1], [CategoriesMapping(mapping)]) self.assertEqual(model.data(model.index(0, 0), MultiplicityRole), 1) self.assertEqual(model.data(model.index(1, 0), MultiplicityRole), 2) self.assertEqual(model.data(model.index(2, 0), MultiplicityRole), 2) w.grab() model.setData(model.index(0, 0), "b", Qt.EditRole) self.assertEqual(model.data(model.index(0, 0), MultiplicityRole), 3) self.assertEqual(model.data(model.index(1, 0), MultiplicityRole), 3) self.assertEqual(model.data(model.index(2, 0), MultiplicityRole), 3) w.grab() def test_discrete_editor_add_remove_action(self): w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b"))) w.set_data(v) action_add = w.add_new_item action_remove = w.remove_item view = w.values_edit model, selection = view.model(), view.selectionModel() selection.clear() action_add.trigger() self.assertTrue(view.state() == view.EditingState) editor = view.focusWidget() assert isinstance(editor, QLineEdit) spy = QSignalSpy(model.dataChanged) QTest.keyClick(editor, Qt.Key_D) QTest.keyClick(editor, Qt.Key_Return) self.assertTrue(model.rowCount() == 4) # The commit to model is executed via a queued invoke self.assertTrue(bool(spy) or spy.wait()) self.assertEqual(model.index(3, 0).data(Qt.EditRole), "d") # remove it spy = QSignalSpy(model.rowsRemoved) action_remove.trigger() self.assertEqual(model.rowCount(), 3) self.assertEqual(len(spy), 1) _, first, last = spy[0] self.assertEqual((first, last), (3, 3)) # remove/drop and existing value selection.select(model.index(1, 0), QItemSelectionModel.ClearAndSelect) removespy = QSignalSpy(model.rowsRemoved) changedspy = QSignalSpy(model.dataChanged) action_remove.trigger() self.assertEqual(len(removespy), 0, "Should only mark item as removed") self.assertGreaterEqual(len(changedspy), 1, "Did not change data") w.grab() def test_discrete_editor_merge_action(self): w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b"))) w.set_data(v) action = w.merge_items self.assertFalse(action.isEnabled()) view = w.values_edit model = view.model() selmodel = view.selectionModel() # type: QItemSelectionModel selmodel.select(QItemSelection(model.index(0, 0), model.index(1, 0)), QItemSelectionModel.ClearAndSelect) self.assertTrue(action.isEnabled()) # trigger the action, then find the active popup, and simulate entry spy = QSignalSpy(w.variable_changed) w.merge_items.trigger() cb = w.findChild(QComboBox) cb.setCurrentText("BA") cb.activated[str].emit("BA") cb.close() self.assertEqual(model.index(0, 0).data(Qt.EditRole), "BA") self.assertEqual(model.index(1, 0).data(Qt.EditRole), "BA") self.assertSequenceEqual(list(spy), [[]], 'variable_changed should emit exactly once') def test_time_editor(self): w = TimeVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Time("T", (("A", "1"), ("B", "b"))) w.set_data(v, ) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) DataVectors = [ CategoricalVector( Categorical("A", ("a", "aa"), ()), lambda: MArray([0, 1, 2], mask=[False, False, True])), RealVector(Real("B", (6, "f"), ()), lambda: MArray([0.1, 0.2, 0.3], mask=[True, False, True])), TimeVector( Time("T", ()), lambda: MArray( [0, 100, 200], dtype="M8[us]", mask=[True, False, True])), StringVector( String("S", ()), lambda: MArray( ["0", "1", "2"], dtype=object, mask=[True, False, True])), ] ReinterpretTransforms = { Categorical: AsCategorical, Real: AsContinuous, Time: AsTime, String: AsString } def test_reinterpret_editor(self): w = ReinterpretVariableEditor() self.assertEqual(w.get_data(), (None, [])) data = self.DataVectors[0] w.set_data(data, ) self.assertEqual(w.get_data(), (data.vtype, [])) w.set_data(data, [Rename("Z")]) self.assertEqual(w.get_data(), (data.vtype, [Rename("Z")])) for vec, tr in product(self.DataVectors, self.ReinterpretTransforms.values()): w.set_data(vec, [tr()]) v, tr_ = w.get_data() self.assertEqual(v, vec.vtype) if not tr_: self.assertEqual(tr, self.ReinterpretTransforms[type(v)]) else: self.assertEqual(tr_, [tr()]) def test_reinterpret_editor_simulate(self): w = ReinterpretVariableEditor() tc = w.findChild(QComboBox, name="type-combo") def cb(): var, tr = w.get_data() type_ = tc.currentData() if type_ is not type(var): self.assertEqual( tr, [self.ReinterpretTransforms[type_](), Rename("Z")]) else: self.assertEqual(tr, [Rename("Z")]) for vec in self.DataVectors: w.set_data(vec, [Rename("Z")]) simulate.combobox_run_through_all(tc, callback=cb)
class TestEditors(GuiTest): def test_variable_editor(self): w = VariableEditor() self.assertEqual(w.get_data(), (None, [])) v = String("S", (("A", "1"), ("B", "b")), False) w.set_data(v, []) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), {"A": "1", "B": "b"}) self.assertEqual(w.get_data(), (v, [])) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) w.set_data(v, [Rename("T"), Annotate((("a", "1"), ("b", "2")))]) self.assertEqual(w.name_edit.text(), "T") self.assertEqual(w.labels_model.rowCount(), 2) add = w.findChild(QAction, "action-add-label") add.trigger() remove = w.findChild(QAction, "action-delete-label") remove.trigger() def test_continuous_editor(self): w = ContinuousVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Real("X", (-1, ""), (("A", "1"), ("B", "b")), False) w.set_data(v, []) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) def test_discrete_editor(self): w = DiscreteVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b")), False) values = [0, 0, 0, 1, 1, 2] w.set_data_categorical(v, values) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) self.assertEqual(w.get_data(), (v, [])) w.set_data_categorical(None, None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) mapping = [("c", "C"), ("a", "A"), ("b", None), (None, "b")] w.set_data_categorical(v, values, [CategoriesMapping(mapping)]) w.grab() # run delegate paint method self.assertEqual(w.get_data(), (v, [CategoriesMapping(mapping)])) # test selection/deselection in the view w.set_data_categorical(v, values) view = w.values_edit model = view.model() assert model.rowCount() sel_model = view.selectionModel() model = sel_model.model() sel_model.select(model.index(0, 0), QItemSelectionModel.Select) sel_model.select(model.index(0, 0), QItemSelectionModel.Deselect) # merge mapping mapping = [("a", "a"), ("b", "b"), ("c", "b")] w.set_data_categorical(v, values, [CategoriesMapping(mapping)]) self.assertEqual(w.get_data()[1], [CategoriesMapping(mapping)]) self.assertEqual(model.data(model.index(0, 0), MultiplicityRole), 1) self.assertEqual(model.data(model.index(1, 0), MultiplicityRole), 2) self.assertEqual(model.data(model.index(2, 0), MultiplicityRole), 2) w.grab() model.setData(model.index(0, 0), "b", Qt.EditRole) self.assertEqual(model.data(model.index(0, 0), MultiplicityRole), 3) self.assertEqual(model.data(model.index(1, 0), MultiplicityRole), 3) self.assertEqual(model.data(model.index(2, 0), MultiplicityRole), 3) w.grab() def test_discrete_editor_add_remove_action(self): w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b")), False) values = [0, 0, 0, 1, 1, 2] w.set_data_categorical(v, values) action_add = w.add_new_item action_remove = w.remove_item view = w.values_edit model, selection = view.model(), view.selectionModel() selection.clear() action_add.trigger() self.assertTrue(view.state() == view.EditingState) editor = view.focusWidget() assert isinstance(editor, QLineEdit) spy = QSignalSpy(model.dataChanged) QTest.keyClick(editor, Qt.Key_D) QTest.keyClick(editor, Qt.Key_Return) self.assertTrue(model.rowCount() == 4) # The commit to model is executed via a queued invoke self.assertTrue(bool(spy) or spy.wait()) self.assertEqual(model.index(3, 0).data(Qt.EditRole), "d") # remove it spy = QSignalSpy(model.rowsRemoved) action_remove.trigger() self.assertEqual(model.rowCount(), 3) self.assertEqual(len(spy), 1) _, first, last = spy[0] self.assertEqual((first, last), (3, 3)) # remove/drop and existing value selection.select(model.index(1, 0), QItemSelectionModel.ClearAndSelect) removespy = QSignalSpy(model.rowsRemoved) changedspy = QSignalSpy(model.dataChanged) action_remove.trigger() self.assertEqual(len(removespy), 0, "Should only mark item as removed") self.assertGreaterEqual(len(changedspy), 1, "Did not change data") w.grab() # mocking exec make dialog never show - dialog blocks running until closed @patch("Orange.widgets.data.oweditdomain.GroupItemsDialog.exec", Mock(side_effect=lambda: QDialog.Accepted)) def test_discrete_editor_merge_action(self): """ This function check whether results of dialog have effect on merging the attributes. The dialog itself is tested separately. """ w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b")), False) w.set_data_categorical( v, [0, 0, 0, 1, 1, 2], [CategoriesMapping([ ("a", "AA"), ("b", "BB"), ("c", "CC"), ])]) view = w.values_edit model = view.model() selmodel = view.selectionModel() # type: QItemSelectionModel selmodel.select(QItemSelection(model.index(0, 0), model.index(1, 0)), QItemSelectionModel.ClearAndSelect) spy = QSignalSpy(w.variable_changed) w.merge_items.trigger() self.assertEqual(model.index(0, 0).data(Qt.EditRole), "other") self.assertEqual(model.index(1, 0).data(Qt.EditRole), "other") self.assertEqual(model.index(2, 0).data(Qt.EditRole), "CC") self.assertSequenceEqual(list(spy), [[]], 'variable_changed should emit exactly once') def test_discrete_editor_rename_selected_items_action(self): w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b")), False) w.set_data_categorical(v, []) action = w.rename_selected_items view = w.values_edit model = view.model() selmodel = view.selectionModel() # type: QItemSelectionModel selmodel.select(QItemSelection(model.index(0, 0), model.index(1, 0)), QItemSelectionModel.ClearAndSelect) # trigger the action, then find the active popup, and simulate entry spy = QSignalSpy(w.variable_changed) with patch.object(QComboBox, "setVisible", return_value=None) as m: action.trigger() m.assert_called() cb = view.findChild(QComboBox) cb.setCurrentText("BA") view.commitData(cb) self.assertEqual(model.index(0, 0).data(Qt.EditRole), "BA") self.assertEqual(model.index(1, 0).data(Qt.EditRole), "BA") self.assertSequenceEqual(list(spy), [[]], 'variable_changed should emit exactly once') def test_discrete_editor_context_menu(self): w = DiscreteVariableEditor() v = Categorical("C", ("a", "b", "c"), (("A", "1"), ("B", "b")), False) w.set_data_categorical(v, []) view = w.values_edit model = view.model() pos = view.visualRect(model.index(0, 0)).center() with patch.object(QMenu, "setVisible", return_value=None) as m: contextMenu(view.viewport(), pos) m.assert_called() menu = view.findChild(QMenu) self.assertIsNotNone(menu) menu.close() def test_time_editor(self): w = TimeVariableEditor() self.assertEqual(w.get_data(), (None, [])) v = Time("T", (("A", "1"), ("B", "b")), False) w.set_data(v, ) self.assertEqual(w.name_edit.text(), v.name) self.assertEqual(w.labels_model.get_dict(), dict(v.annotations)) w.set_data(None) self.assertEqual(w.name_edit.text(), "") self.assertEqual(w.labels_model.get_dict(), {}) self.assertEqual(w.get_data(), (None, [])) DataVectors = [ CategoricalVector( Categorical("A", ("a", "aa"), (), False), lambda: MArray([0, 1, 2], mask=[False, False, True])), RealVector(Real("B", (6, "f"), (), False), lambda: MArray([0.1, 0.2, 0.3], mask=[True, False, True])), TimeVector( Time("T", (), False), lambda: MArray( [0, 100, 200], dtype="M8[us]", mask=[True, False, True])), StringVector( String("S", (), False), lambda: MArray( ["0", "1", "2"], dtype=object, mask=[True, False, True])), ] ReinterpretTransforms = { Categorical: AsCategorical, Real: AsContinuous, Time: AsTime, String: AsString } def test_reinterpret_editor(self): w = ReinterpretVariableEditor() self.assertEqual(w.get_data(), (None, [])) data = self.DataVectors[0] w.set_data(data, ) self.assertEqual(w.get_data(), (data.vtype, [])) w.set_data(data, [Rename("Z")]) self.assertEqual(w.get_data(), (data.vtype, [Rename("Z")])) for vec, tr in product(self.DataVectors, self.ReinterpretTransforms.values()): w.set_data(vec, [tr()]) v, tr_ = w.get_data() self.assertEqual(v, vec.vtype) if not tr_: self.assertEqual(tr, self.ReinterpretTransforms[type(v)]) else: self.assertEqual(tr_, [tr()]) def test_reinterpret_editor_simulate(self): w = ReinterpretVariableEditor() tc = w.findChild(QComboBox, name="type-combo") def cb(): var, tr = w.get_data() type_ = tc.currentData() if type_ is not type(var): self.assertEqual( tr, [self.ReinterpretTransforms[type_](), Rename("Z")]) else: self.assertEqual(tr, [Rename("Z")]) for vec in self.DataVectors: w.set_data(vec, [Rename("Z")]) simulate.combobox_run_through_all(tc, callback=cb) def test_unlink(self): w = ContinuousVariableEditor() cbox = w.unlink_var_cb self.assertEqual(w.get_data(), (None, [])) v = Real("X", (-1, ""), (("A", "1"), ("B", "b")), False) w.set_data(v, []) self.assertFalse(cbox.isEnabled()) v = Real("X", (-1, ""), (("A", "1"), ("B", "b")), True) w.set_data(v, [Unlink()]) self.assertTrue(cbox.isEnabled()) self.assertTrue(cbox.isChecked()) v = Real("X", (-1, ""), (("A", "1"), ("B", "b")), True) w.set_data(v, []) self.assertTrue(cbox.isEnabled()) self.assertFalse(cbox.isChecked()) cbox.setChecked(True) self.assertEqual(w.get_data()[1], [Unlink()]) w.set_data(v, [Unlink()]) self.assertTrue(cbox.isChecked()) cbox.setChecked(False) self.assertEqual(w.get_data()[1], []) cbox.setChecked(True) w.clear() self.assertFalse(cbox.isChecked()) self.assertEqual(w.get_data()[1], []) w._set_unlink(True) self.assertTrue(cbox.isChecked()) w._set_unlink(False) self.assertFalse(cbox.isChecked())