-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
For remembering states of combo boxes where we choose features we tend to use contexts. We frequently use DomainContext
, which works well most of the time.
It works so well that I did not notice, until now, that it is not appropriate if combo boxes within the widget are limited to a different subset of (attributes, class_vars, metas).
Here is an example:
from Orange.data import Table, Domain
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.widget import OWWidget
from Orange.widgets.gui import comboBox
from Orange.widgets.settings import \
ContextSetting, DomainContextHandler, PerfectDomainContextHandler
from Orange.widgets.utils.itemmodels import DomainModel
from orangewidget.utils.signals import Input
class OWProblem(OWWidget):
name = "Context Problem"
v1 = ContextSetting(None)
v2 = ContextSetting(None)
settingsHandler = DomainContextHandler()
class Inputs:
data = Input("Data", Table, default=True)
def __init__(self):
super().__init__()
m1 = DomainModel(DomainModel.SEPARATED,
valid_types=DomainModel.PRIMITIVE)
comboBox(self.controlArea, self, "v1", model=m1, label="All")
m2 = DomainModel(DomainModel.METAS | DomainModel.CLASSES,
valid_types=DomainModel.PRIMITIVE)
comboBox(self.controlArea, self, "v2", model=m2, label="Metas + Classes")
self.contextAboutToBeOpened.connect(lambda x: self.init_interface(x[0]))
def init_interface(self, data):
domain = data.domain if data is not None else None
self.controls.v1.model().set_domain(domain)
self.controls.v2.model().set_domain(domain)
@Inputs.data
def set_data(self, data):
self.closeContext()
self.openContext(data)
class TestOWProblem(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWProblem)
def test_v2_invalid_after_reloading(self):
iris = Table("iris")
petals_in_metas = Domain(iris.domain.attributes[:2],
iris.domain.class_var,
metas=iris.domain.attributes[2:])
self.send_signal(self.widget.Inputs.data,
iris.transform(petals_in_metas))
self.widget.v1 = iris.domain["sepal width"]
# v2 can have only metas and classes thus this will be invalid
# context for original iris
self.widget.v2 = iris.domain["petal width"]
self.send_signal(self.widget.Inputs.data, iris) # crashes
Running the test gives:
File "/home/marko/dev/orange-spectroscopy/orangecontrib/spectroscopy/widgets/owproblem.py", line 42, in set_data
self.openContext(data)
File "/home/marko/dev/orange-widget-base/orangewidget/widget.py", line 1350, in openContext
self.settingsHandler.open_context(self, *a)
File "/home/marko/dev/orange3/Orange/widgets/settings.py", line 125, in open_context
super().open_context(widget, domain, *self.encode_domain(domain))
File "/home/marko/dev/orange-widget-base/orangewidget/settings.py", line 833, in open_context
self.settings_to_widget(widget, *args)
File "/home/marko/dev/orange-widget-base/orangewidget/settings.py", line 946, in settings_to_widget
_apply_setting(setting, instance, value)
File "/home/marko/dev/orange-widget-base/orangewidget/settings.py", line 204, in _apply_setting
setattr(instance, setting.name, value)
File "/home/marko/dev/orange-widget-base/orangewidget/gui.py", line 194, in __setattr__
callback(value)
File "/home/marko/dev/orange-widget-base/orangewidget/gui.py", line 2320, in __call__
self.action(*args)
File "/home/marko/dev/orange-widget-base/orangewidget/gui.py", line 2407, in action
raise ValueError("Combo box does not contain item " + repr(value))
ValueError: Combo box does not contain item ContinuousVariable(name='petal width', number_of_decimals=1)
Is this a bug in DomainContextHandler? No, it is how it was designed; it is just that I was using it wrongly. For this kind of situations, I'll need a new type of context. PerfectDomainContextHandler
does not exhibit the same issue but is too restrictive. I'll need something in between.
It is a bug that contexts that match and could not be opened effectively (for any reason) block you from using the widget with that data? Probably. :) When this happens, the only solutions is to clear the settings.