Example #1
0
    def test_delegate(self):
        model = VariableListModel([None, None])

        def set_item(row: int, v: dict):
            model.setItemData(model.index(row), v)

        def get_style_option(row: int) -> QStyleOptionViewItem:
            opt = QStyleOptionViewItem()
            delegate.initStyleOption(opt, model.index(row))
            return opt

        set_item(0, {Qt.EditRole: Categorical("a", (), (), False)})
        delegate = VariableEditDelegate()
        opt = get_style_option(0)
        self.assertEqual(opt.text, "a")
        self.assertFalse(opt.font.italic())
        set_item(0, {TransformRole: [Rename("b")]})
        opt = get_style_option(0)
        self.assertEqual(opt.text, "a \N{RIGHTWARDS ARROW} b")
        self.assertTrue(opt.font.italic())

        set_item(0, {TransformRole: [AsString()]})
        opt = get_style_option(0)
        self.assertIn("reinterpreted", opt.text)
        self.assertTrue(opt.font.italic())
        set_item(1, {
            Qt.EditRole: String("b", (), False),
            TransformRole: [Rename("a")]
        })
        opt = get_style_option(1)
        self.assertEqual(opt.palette.color(QPalette.Text), QColor(Qt.red))
        view = QListView()
        with patch.object(QToolTip, "showText") as p:
            delegate.helpEvent(
                QHelpEvent(QHelpEvent.ToolTip, QPoint(0, 0), QPoint(0, 0)),
                view,
                opt,
                model.index(1),
            )
            p.assert_called_once()
Example #2
0
    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()
Example #3
0
 def test_reinterpret(self):
     var = String("T", ())
     for tr in (AsContinuous(), AsCategorical(), AsTime()):
         t = report_transform(var, [tr])
         self.assertIn("→ (", t)
Example #4
0
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())