-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ENH] OWSelectAttributes: Use input features #3299
Conversation
a692e48
to
54c2ce7
Compare
Codecov Report
@@ Coverage Diff @@
## master #3299 +/- ##
=========================================
+ Coverage 82.12% 82.2% +0.07%
=========================================
Files 351 351
Lines 62122 62334 +212
=========================================
+ Hits 51017 51240 +223
+ Misses 11105 11094 -11 |
54c2ce7
to
6a8386b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works as intended, except for one thing: the push button disables the list view, so the only way to get out of this is to use the Reset button. Only the checkbox should disable the view. See the code I propose below.
I like it that the button is enabled exactly when it would actually do something.
I don't like the GUI we decided upon: the checkbox and the push button now dominate the widget. As much as I prefer disabling instead of hiding, I lean towards disabling it when input features are not present.
The remaining comments I wrote are just nitpicking. Use or ignore at will.
if self.use_input_features and self.can_use_features(): | ||
self.use_features() | ||
if not self.use_input_features: | ||
self.enable_use_features_box() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose I would want to have the list view disabled only when checkbox is checked:
if self.use_input_features and self.can_use_features():
self.use_features()
self.enable_used_attrs(False)
else:
self.enable_used_attrs()
Otherwise, pushing the button disabled the list view and buttons, and the only action that gets me out of this is the Reset button.
self.use_features_box.repaint() | ||
|
||
def use_features(self): | ||
self.enable_used_attrs(False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the same reason as above, in __use_features_changed
, I think the above line should be removed. Pushing the button should keep the list view enabled, otherwise the only option to re-enable it is the Reset button.
if feature.name in domain and domain[feature.name] | ||
in domain.attributes] | ||
|
||
def can_use_features(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the first glance, I thought that this method checks whether the features on the input are usable. I don't have a better name. Perhaps can_enable_use_features
? I don't like it, but it tells me what it does.
return bool(self.features_from_data_attributes) and \ | ||
self.features_from_data_attributes != self.used_attrs[:] | ||
|
||
def __use_features_changed(self): # Use input features check box |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name confuses me, too. Perhaps __on_use_features_checkbox
? Not too nice, either, but I understand it.
self.Warning.mismatching_domain.clear() | ||
if self.data is not None and self.features is not None and \ | ||
not len(self.features_from_data_attributes): | ||
self.Warning.mismatching_domain() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also do:
def check_data(self):
self.Warning.mismatching_domain(
shown=self.data is not None
and self.features is not None
and not self.features_from_data_attributes
)
domain = self.data.domain | ||
return [domain[feature.name] for feature in self.features | ||
if feature.name in domain and domain[feature.name] | ||
in domain.attributes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a matter of taste, but consider avoiding repeating domain[feature.name]
by
return [var
for var in (domain[feature.name]
for feature in self.features if feature.name in domain)
if var in domain.attributes]
or
existing = (domain[feature.name] for feature in self.features
if feature.name in domain)
return [var for var in existing if var in domain.attributes]
On the other hand, isn't this the same as
attrs = {attr.name for attr in domain.attributes}
return [domain[feature.name] if feature.name in attrs]
assuming that all names are unique?
"Use input features", "Always use input features", | ||
box=False, commit=self.__use_features_clicked, | ||
callback=self.__use_features_changed, addToLayout=False | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize you can repurpose gui.auto_commit
for this. I assumed there can be only a single auto_commit
control per widget. :) Nice.
self.check_data() | ||
self.enable_used_attrs() | ||
self.enable_use_features_box() | ||
if self.use_input_features and len(self.features_from_data_attributes): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since self.features_from_data_attributes
is a list, PEP8 recommends if self.use_input_features and self.features_from_data_attributes
self.move_attr_button.setEnabled(enable) | ||
self.down_attr_button.setEnabled(enable) | ||
self.used_attrs_view.setEnabled(enable) | ||
self.used_attrs_view.repaint() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that these repaints (here and in the next function) are needed? It seems to work without for me.
def enable_use_features_box(self): | ||
self.use_features_box.button.setEnabled(self.can_use_features()) | ||
enable_checkbox = bool(self.features_from_data_attributes) | ||
self.use_features_box.checkbox.setEnabled(enable_checkbox) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps self.use_features_box.setHidden(not enable_checkbox)
(see the general comment).
cfa7b34
to
dbf75bc
Compare
dbf75bc
to
c988589
Compare
Issue
Enable filtering dataset using selected features.
Implements #3300
Description of changes
Upgrade Select Columns widget to accept Features on the input (as AttributeList). One can choose between using input features and data domain features to obtain output data. New button "Use input features" is added for this purpose. When features appear on the input, the button enables. After the features have been used, the "Features" list box disables.
The initial state is can be obtained by clicking "Reset" button.
Upgrade Rank widget to output Features (as AttributeList).
Includes