Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 deletions Orange/widgets/data/owcreateclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from Orange.statistics.util import bincount
from Orange.preprocess.transformation import Transformation, Lookup
from Orange.widgets import gui, widget
from Orange.widgets.settings import DomainContextHandler, ContextSetting
from Orange.widgets.settings import DomainContextHandler, ContextSetting, Setting
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.widgets.utils.localization import pl
from Orange.widgets.utils.widgetpreview import WidgetPreview
Expand Down Expand Up @@ -229,12 +229,14 @@ class Outputs:
buttons_area_orientation = Qt.Vertical

settingsHandler = DomainContextHandler()
attribute = ContextSetting(None)
class_name = ContextSetting("class")
rules = ContextSetting({})
match_beginning = ContextSetting(False)
case_sensitive = ContextSetting(False)
regular_expressions = ContextSetting(False)
attribute = ContextSetting(None, schema_only=True)
class_name = Setting("class", schema_only=True)
rules = Setting({}, schema_only=True)
match_beginning = Setting(False, schema_only=True)
case_sensitive = Setting(False, schema_only=True)
regular_expressions = Setting(False, schema_only=True)

settings_version = 2

TRANSFORMERS = {StringVariable: ValueFromStringSubstring,
DiscreteVariable: ValueFromDiscreteSubstring}
Expand Down Expand Up @@ -270,15 +272,15 @@ def __init__(self):
#: list of list of QLabel: pairs of labels with counts
self.counts = []

gui.lineEdit(
le = gui.lineEdit(
self.controlArea, self, "class_name",
orientation=Qt.Horizontal, box="New Class Name")
le.setStyleSheet("QLineEdit { padding-left: 4px; }")

variable_select_box = gui.vBox(self.controlArea, "Match by Substring")
variable_select_box = gui.vBox(self.controlArea, box="Source column and patterns")

combo = gui.comboBox(
variable_select_box, self, "attribute", label="From column:",
orientation=Qt.Horizontal, searchable=True,
variable_select_box, self, "attribute", searchable=True,
callback=self.update_rules,
model=DomainModel(valid_types=(StringVariable, DiscreteVariable)))
# Don't use setSizePolicy keyword argument here: it applies to box,
Expand Down Expand Up @@ -328,8 +330,8 @@ def __init__(self):

gui.button(self.buttonsArea, self, "Apply", callback=self.apply)

# TODO: Resizing upon changing the number of rules does not work
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
self.update_dynamic_height(initial=True)

@property
def active_rules(self):
Expand All @@ -347,11 +349,16 @@ def rules_to_edits(self):
for edit, text in zip(editr, textr):
edit.setText(text)

def update_dynamic_height(self, initial=False):
self.updateGeometry()
current_width = 350 if initial else self.width()
target_height = self.layout().sizeHint().height()
self.resize(current_width, target_height)

@Inputs.data
def set_data(self, data):
"""Input data signal handler."""
self.closeContext()
self.rules = {}
self.data = data
model = self.controls.attribute.model()
model.set_domain(data.domain if data is not None else None)
Expand Down Expand Up @@ -693,6 +700,18 @@ def _count_part():
self.report_items("Output", [("Class name", self.class_name)])
self.report_raw(f"<ol>{output}</ol>")

@classmethod
def migrate_settings(cls, settings, version):
if version < 2:
contexts = settings.pop("context_settings", [])
if contexts:
print(contexts[0].values)
print(settings)
settings.update(
{name: contexts[0].values[name][0]
for name in ("class_name", "rules", "match_beginning",
"case_sensitive")})


if __name__ == "__main__": # pragma: no cover
WidgetPreview(OWCreateClass).run(Table("zoo"))
50 changes: 28 additions & 22 deletions Orange/widgets/data/tests/test_owcreateclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np

from orangewidget.settings import Context
from Orange.data import Table, StringVariable, DiscreteVariable, Domain
from Orange.widgets.data.owcreateclass import (
OWCreateClass,
Expand Down Expand Up @@ -503,7 +504,7 @@ def _check_thal(self):
np.testing.assert_equal(classes[fixed], 1)
self.assertTrue(np.all(np.isnan(classes[~(reversable | fixed)])))

def test_flow_and_context_handling(self):
def test_flow(self):
widget = self.widget
self.send_signal(self.widget.Inputs.data, self.heart)
self._test_default_rules()
Expand Down Expand Up @@ -545,27 +546,6 @@ def test_flow_and_context_handling(self):
self._set_attr(thal)
self._check_thal()

prev_rules = widget.rules
self.send_signal(self.widget.Inputs.data, self.zoo)
self.assertIsNot(widget.rules, prev_rules)

self.send_signal(self.widget.Inputs.data, self.heart)
self._check_thal()

# Check that sending None as data does not ruin the context, and that
# the empty context does not match the true one later
self.send_signal(self.widget.Inputs.data, None)
self.assertIsNot(widget.rules, prev_rules)

self.send_signal(self.widget.Inputs.data, self.heart)
self._check_thal()

self.send_signal(self.widget.Inputs.data, self.no_attributes)
self.assertIsNot(widget.rules, prev_rules)

self.send_signal(self.widget.Inputs.data, self.heart)
self._check_thal()

def test_add_remove_lines(self):
widget = self.widget
self.send_signal(self.widget.Inputs.data, self.heart)
Expand Down Expand Up @@ -690,6 +670,32 @@ def test_same_class(self):
self.get_output(widget2.Outputs.data, widget=widget2).domain.class_var
)

def test_migrate_settings_1_2(self):
settings = {"__version__": 1, "context_settings": [Context(
values= {
'attribute': ('type', 101),
'case_sensitive': (True, -2),
'class_name': ('class', -2),
'match_beginning': (True, -2),
'regular_expressions': (False, -2),
'rules': ({'type': [['cam', 'am'], ['cer', 'er'], ['', '']],
'eggs': [['de', 'e1'], ['', '']]},
-2),
'__version__': 1},
attributes = {'hair': 1, 'feathers': 1, 'eggs': 1, 'type': 1},
metas = {'name': 3})]}
w = self.create_widget(OWCreateClass, stored_settings=settings)
self.send_signal(w.Inputs.data, self.zoo, widget=w)
self.assertEqual(w.active_rules, [['cam', 'am'], ['cer', 'er'], ['', '']])
self.assertEqual(w.class_name, "class")
self.assertTrue(w.case_sensitive)
self.assertTrue(w.match_beginning)
self.assertFalse(w.regular_expressions)

w.attribute = self.zoo.domain["eggs"]
self.assertEqual(w.active_rules, [['de', 'e1'], ['', '']])



if __name__ == "__main__":
unittest.main()
10 changes: 8 additions & 2 deletions i18n/si/msgs.jaml
Original file line number Diff line number Diff line change
Expand Up @@ -4820,9 +4820,9 @@ widgets/data/owcreateclass.py:
def `__init__`:
class_name: false
New Class Name: Ime novega razreda
Match by Substring: Vzorci in razredi
'QLineEdit { padding-left: 4px; }': false
Source column and patterns: Izvorni stolpec in vzorci
attribute: false
From column:: Iz stolpca:
Name: Ime
Substring: Vzorec
Count: Primerov
Expand Down Expand Up @@ -4873,6 +4873,12 @@ widgets/data/owcreateclass.py:
Output: Izhod
Class name: Ime razreda
<ol>{output}</ol>: false
def `migrate_settings`:
context_settings: false
class_name: false
rules: false
match_beginning: false
case_sensitive: false
__main__: false
zoo: false
widgets/data/owcreateinstance.py:
Expand Down