-
-
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) # crashesRunning 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.