8000 Use features instead of properties internally by andy-sweet · Pull Request #7084 · napari/napari · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Use features instead of properties internally #7084

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
22 changes: 11 additions & 11 deletions napari/_qt/layer_controls/_tests/test_qt_layer_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class LayerTypeWithData(NamedTuple):
type: type[Layer]
data: np.ndarray
colormap: Optional[DirectLabelColormap]
properties: Optional[dict]
features: Optional[dict]
expected_isinstance: type[QtLayerControlsContainer]


Expand All @@ -62,7 +62,7 @@ class LayerTypeWithData(NamedTuple):
type=Image,
data=np.random.rand(8, 8),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtImageControls,
)
_LABELS_WITH_DIRECT_COLORMAP = LayerTypeWithData(
Expand All @@ -78,28 +78,28 @@ class LayerTypeWithData(NamedTuple):
None: 'black',
}
),
properties=None,
features=None,
expected_isinstance=QtLabelsControls,
)
_LABELS = LayerTypeWithData(
type=Labels,
data=np.random.randint(5, size=(10, 15)),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtLabelsControls,
)
_POINTS = LayerTypeWithData(
type=Points,
data=np.random.random((5, 2)),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtPointsControls,
)
_SHAPES = LayerTypeWithData(
type=Shapes,
data=np.random.random((10, 4, 2)),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtShapesControls,
)
_SURFACE = LayerTypeWithData(
Expand All @@ -110,14 +110,14 @@ class LayerTypeWithData(NamedTuple):
np.random.random(10),
),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtSurfaceControls,
)
_TRACKS = LayerTypeWithData(
type=Tracks,
data=np.zeros((2, 4)),
colormap=None,
properties={
features={
'track_id': [0, 0],
'time': [0, 0],
'speed': [50, 30],
Expand All @@ -128,7 +128,7 @@ class LayerTypeWithData(NamedTuple):
type=Vectors,
data=np.zeros((2, 2, 2)),
colormap=None,
properties=None,
features=None,
expected_isinstance=QtVectorsControls,
)
_LINES_DATA = np.random.random((6, 2, 2))
Expand All @@ -142,10 +142,10 @@ def _create_layer_controls(layer_type_with_data):
layer_type_with_data.data,
colormap=layer_type_with_data.colormap,
)
elif layer_type_with_data.properties:
elif layer_type_with_data.features:
layer = layer_type_with_data.type(
layer_type_with_data.data,
properties=layer_type_with_data.properties,
features=layer_type_with_data.features,
)
else:
layer = layer_type_with_data.type(layer_type_with_data.data)
Expand Down
42 changes: 19 additions & 23 deletions napari/_qt/layer_controls/_tests/test_qt_tracks_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,26 @@ def null_data() -> np.ndarray:


@pytest.fixture()
def properties() -> dict[str, list]:
def features() -> dict[str, list]:
return {
'track_id': [0, 0],
'time': [0, 0],
'speed': [50, 30],
}


def test_tracks_controls_color_by(null_data, properties, qtbot):
def test_tracks_controls_color_by(null_data, features, qtbot):
"""Check updating of the color_by combobox."""
inital_color_by = 'time'
initial_color_by = 'time'
with pytest.warns(UserWarning) as wrn:
layer = Tracks(
null_data, properties=properties, color_by=inital_color_by
)
layer = Tracks(null_data, features=features, color_by=initial_color_by)
assert "Previous color_by key 'time' not present" in str(wrn[0].message)
qtctrl = QtTracksControls(layer)
qtbot.addWidget(qtctrl)

# verify the color_by argument is initialized correctly
assert layer.color_by == inital_color_by
assert qtctrl.color_by_combobox.currentText() == inital_color_by
assert layer.color_by == initial_color_by
assert qtctrl.color_by_combobox.currentText() == initial_color_by

# update color_by from the layer model
layer_update_color_by = 'speed'
Expand All @@ -52,44 +50,42 @@ def test_tracks_controls_color_by(null_data, properties, qtbot):


@pytest.mark.parametrize('color_by', ['track_id', 'speed'])
def test_color_by_same_after_properties_change(
null_data, properties, color_by, qtbot
def test_color_by_same_after_features_change(
null_data, features, color_by, qtbot
):
"""See https://github.com/napari/napari/issues/5330"""
layer = Tracks(null_data, properties=properties)
layer = Tracks(null_data, features=features)
layer.color_by = color_by
controls = QtTracksControls(layer)
qtbot.addWidget(controls)
assert controls.color_by_combobox.currentText() == color_by

# Change the properties value by removing the time column.
layer.properties = {
'track_id': properties['track_id'],
'speed': properties['speed'],
# Change the features value by removing the time column.
layer.features = {
'track_id': features['track_id'],
'speed': features['speed'],
}

assert layer.color_by == color_by
assert controls.color_by_combobox.currentText() == color_by


def test_color_by_missing_after_properties_change(
null_data, properties, qtbot
):
def test_color_by_missing_after_features_change(null_data, features, qtbot):
"""See https://github.com/napari/napari/issues/5330"""
layer = Tracks(null_data, properties=properties)
layer = Tracks(null_data, features=features)
layer.color_by = 'time'
controls = QtTracksControls(layer)
qtbot.addWidget(controls)
assert controls.color_by_combobox.currentText() == 'time'

# Change the properties value by removing the time column.
# Change the features value by removing the time column.
with pytest.warns(
UserWarning,
match="Previous color_by key 'time' not present in features. Falling back to track_id",
):
layer.properties = {
'track_id': properties['track_id'],
'speed': properties['speed'],
layer.features = {
'track_id': features['track_id'],
'speed': features['speed'],
}

assert layer.color_by == 'track_id'
Expand Down
12 changes: 6 additions & 6 deletions napari/_qt/layer_controls/qt_tracks_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ def __init__(self, layer) -> None:
self.layer.events.tail_width.connect(self._on_tail_width_change)
self.layer.events.tail_length.connect(self._on_tail_length_change)
self.layer.events.head_length.connect(self._on_head_length_change)
self.layer.events.properties.connect(self._on_properties_change)
self.layer.events.features.connect(self._on_features_change)
self.layer.events.colormap.connect(self._on_colormap_change)

# combo box for track coloring, we can get these from the properties
# combo box for track coloring, we can get these from the features
# keys
self.color_by_combobox = QComboBox()
self.color_by_combobox.addItems(self.layer.properties_to_color_by)
self.color_by_combobox.addItems(self.layer.features_to_color_by)

self.colormap_combobox = QComboBox()
for name, colormap in AVAILABLE_COLORMAPS.items():
Expand Down Expand Up @@ -136,11 +136,11 @@ def _on_head_length_change(self):
value = self.layer.head_length
self.head_length_slider.setValue(value)

def _on_properties_change(self):
"""Change the properties that can be used to color the tracks."""
def _on_features_change(self):
"""Change the features that can be used to color the tracks."""
with qt_signals_blocked(self.color_by_combobox):
self.color_by_combobox.clear()
self.color_by_combobox.addItems(self.layer.properties_to_color_by)
self.color_by_combobox.addItems(self.layer.features_to_color_by)
self._on_color_by_change()

def _on_colormap_change(self):
Expand Down
68 changes: 26 additions & 42 deletions napari/_qt/layer_controls/qt_vectors_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ class QtVectorsControls(QtLayerControls):
Dropdown widget to select vector_style for the vectors.
color_mode_comboBox : qtpy.QtWidgets.QComboBox
Dropdown widget to select edge_color_mode for the vectors.
color_prop_box : qtpy.QtWidgets.QComboBox
Dropdown widget to select _edge_color_property for the vectors.
edge_prop_label : qtpy.QtWidgets.QLabel
Label for color_prop_box
color_feature_box : qtpy.QtWidgets.QComboBox
Dropdown widget to select the edge_color feature for the vectors.
edge_feature_label : qtpy.QtWidgets.QLabel
Label for color_feature_box
layer : napari.layers.Vectors
An instance of a napari Vectors layer.
outOfSliceCheckBox : qtpy.QtWidgets.QCheckBox
Expand All @@ -78,15 +78,14 @@ class QtVectorsControls(QtLayerControls):
def __init__(self, layer) -> None:
super().__init__(layer)

# dropdown to select the property for mapping edge_color
color_properties = self._get_property_values()
self.color_prop_box = QComboBox(self)
self.color_prop_box.currentTextChanged.connect(
self.change_edge_color_property
# dropdown to select the feature for mapping edge_color
self.color_feature_box = QComboBox(self)
self.color_feature_box.currentTextChanged.connect(
self.change_edge_color_feature
)
self.color_prop_box.addItems(color_properties)
self.color_feature_box.addItems(self.layer.features.columns)

self.edge_prop_label = QLabel(trans._('edge property:'))
self.edge_feature_label = QLabel(trans._('edge feature:'))

# vector direct color mode adjustment and widget
self.edgeColorEdit = QColorSwatchEdit(
Expand Down Expand Up @@ -157,7 +156,7 @@ def __init__(self, layer) -> None:
trans._('edge color mode:'), self.color_mode_comboBox
)
self.layout().addRow(self.edge_color_label, self.edgeColorEdit)
self.layout().addRow(self.edge_prop_label, self.color_prop_box)
self.layout().addRow(self.edge_feature_label, self.color_feature_box)
self.layout().addRow(trans._('out of slice:'), self.outOfSliceCheckBox)

self.layer.events.edge_width.connect(self._on_edge_width_change)
Expand All @@ -171,21 +170,20 @@ def __init__(self, layer) -> None:
)
self.layer.events.edge_color.connect(self._on_edge_color_change)

def change_edge_color_property(self, property_name: str):
"""Change edge_color_property of vectors on the layer model.
This property is the property the edge color is mapped to.
def change_edge_color_feature(self, feature: str):
"""Change edge_color feature of vectors on the layer model.

Parameters
----------
property_name : str
property to map the edge color to
feature : str
feature to map the edge color to
"""
mode = self.layer.edge_color_mode
try:
self.layer.edge_color = property_name
self.layer.edge_color = feature
self.layer.edge_color_mode = mode
except TypeError:
# if the selected property is the wrong type for the current color mode
# if the selected feature is the wrong type for the current color mode
# the color mode will be changed to the appropriate type, so we must update
self._on_edge_color_mode_change()
raise
Expand Down Expand Up @@ -282,30 +280,14 @@ def _update_edge_color_gui(self, mode: str):
if mode in {'cycle', 'colormap'}:
self.edgeColorEdit.setHidden(True)
self.edge_color_label.setHidden(True)
self.color_prop_box.setHidden(False)
self.edge_prop_label.setHidden(False)
self.color_feature_box.setHidden(False)
self.edge_feature_label.setHidden(False)

elif mode == 'direct':
self.edgeColorEdit.setHidden(False)
self.edge_color_label.setHidden(False)
self.color_prop_box.setHidden(True)
self.edge_prop_label.setHidden(True)

def _get_property_values(self):
"""Get the current property values from the Vectors layer

Returns
-------
property_values : np.ndarray
array of all of the union of the property names (keys)
in Vectors.properties and Vectors.property_choices

"""
property_choices = [*self.layer.property_choices]
properties = [*self.layer.properties]
property_values = np.union1d(property_choices, properties)

return property_values
self.color_feature_box.setHidden(True)
self.edge_feature_label.setHidden(True)

def _on_length_change(self):
"""Change length of vectors."""
Expand Down Expand Up @@ -354,7 +336,9 @@ def _on_edge_color_change(self):
ColorMode.CYCLE,
ColorMode.COLORMAP,
):
with qt_signals_blocked(self.color_prop_box):
with qt_signals_blocked(self.color_feature_box):
prop = self.layer._edge.color_properties.name
index = self.color_prop_box.findText(prop, Qt.MatchFixedString)
self.color_prop_box.setCurrentIndex(index)
index = self.color_feature_box.findText(
prop, Qt.MatchFixedString
)
self.color_feature_box.setCurrentIndex(index)
Loading
Loading
0