Skip to content
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

Add support for borders for scatter layers #114

Merged
merged 5 commits into from
Jan 6, 2025
Merged
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
29 changes: 17 additions & 12 deletions glue_plotly/viewers/scatter/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from glue_plotly.common import color_info
from glue_plotly.common.scatter2d import LINESTYLES, rectilinear_lines, scatter_mode, size_info
from glue_plotly.viewers.scatter.state import PlotlyScatterLayerState
from glue.core import BaseData
from glue.core.exceptions import IncompatibleAttribute
from glue.utils import ensure_numerical
from glue.viewers.common.layer_artist import LayerArtist
from glue.viewers.scatter.state import ScatterLayerState

from plotly.graph_objs import Scatter, Scatterpolar

Expand All @@ -18,6 +18,7 @@


CMAP_PROPERTIES = {"cmap_mode", "cmap_att", "cmap_vmin", "cmap_vmax", "cmap"}
BORDER_PROPERTIES = {"border_visible", "border_size", "border_color", "border_color_match_layer"}
MARKER_PROPERTIES = {
"size_mode",
"size_att",
Expand All @@ -31,6 +32,7 @@
VISUAL_PROPERTIES = (
CMAP_PROPERTIES
| MARKER_PROPERTIES
| BORDER_PROPERTIES
| DENSITY_PROPERTIES
| {"color", "alpha", "zorder", "visible"}
)
Expand Down Expand Up @@ -58,7 +60,7 @@

class PlotlyScatterLayerArtist(LayerArtist):

_layer_state_cls = ScatterLayerState
_layer_state_cls = PlotlyScatterLayerState

def __init__(self, view, viewer_state, layer_state=None, layer=None):

Expand Down Expand Up @@ -249,19 +251,22 @@
if self.state.markers_visible:
if force or \
any(prop in changed for prop in CMAP_PROPERTIES) or \
any(prop in changed for prop in BORDER_PROPERTIES) or \
any(prop in changed for prop in ["color", "fill"]):

color = color_info(self.state)
if self.state.fill:
scatter.marker.update(color=color,
line=dict(width=0),
opacity=self.state.alpha)
layer_color = color_info(self.state)
marker_color = layer_color if self.state.fill else "rgba(0, 0, 0, 0)"
if self.state.border_visible:
border_color = layer_color if self.state.border_color_match_layer else self.state.border_color
line = dict(width=self.state.border_size, color=border_color)

Check warning on line 261 in glue_plotly/viewers/scatter/layer_artist.py

View check run for this annotation

Codecov / codecov/patch

glue_plotly/viewers/scatter/layer_artist.py#L260-L261

Added lines #L260 - L261 were not covered by tests
else:
scatter.marker.update(color='rgba(0, 0, 0, 0)',
opacity=self.state.alpha,
line=dict(width=1,
color=color)
)
line = dict(width=0)

scatter.marker.update(
color=marker_color,
line=line,
opacity=self.state.alpha
)

if force or any(prop in changed for prop in MARKER_PROPERTIES):
scatter.marker['size'] = size_info(self.state)
Expand Down
8 changes: 8 additions & 0 deletions glue_plotly/viewers/scatter/layer_state_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from glue_jupyter.common.state_widgets.layer_scatter import ScatterLayerStateWidget
from traitlets import Bool


class PlotlyScatterLayerStateWidget(ScatterLayerStateWidget):

template_file = (__file__, "layer_state_widget.vue")
border_color_menu_open = Bool(False).tag(sync=True)
139 changes: 139 additions & 0 deletions glue_plotly/viewers/scatter/layer_state_widget.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<template>
<div class="glue-plotly-layer-scatter">
<div class="text-subtitle-2 font-weight-bold">Color</div>
<div>
<v-select label="color" :items="cmap_mode_items" v-model="cmap_mode_selected" hide-details />
</div>
<template v-if="glue_state.cmap_mode === 'Linear'">
<div>
<v-select label="attribute" :items="cmap_att_items" v-model="cmap_att_selected" hide-details />
</div>
<div>
<glue-float-field label="min" :value.sync="glue_state.cmap_vmin" />
</div>
<div>
<glue-float-field label="max" :value.sync="glue_state.cmap_vmax" />
</div>
<div>
<v-select label="colormap" :items="cmap_items" :value="glue_state.cmap" @change="set_colormap" hide-details/>
</div>
</template>
<div>
<v-subheader class="pl-0 slider-label">opacity</v-subheader>
<glue-throttled-slider wait="300" min="0" max="1" step="0.01" :value.sync="glue_state.alpha" hide-details />
</div>
<div class="text-subtitle-2 font-weight-bold">Points</div>
<div>
<v-subheader class="pl-0 slider-label">show points</v-subheader>
<v-switch v-model="glue_state.markers_visible" hide-details style="margin-top: 0" />
</div>
<template v-if="glue_state.markers_visible">
<div>
<v-select label="type" :items="points_mode_items" v-model="points_mode_selected" hide-details />
</div>
<div v-if="glue_state.density_map === false">
<v-select label="size" :items="size_mode_items" v-model="size_mode_selected" hide-details />
</div>
<template v-if="glue_state.size_mode === 'Linear'">
<div>
<v-select label="attribute" :items="size_att_items" v-model="size_att_selected" hide-details />
</div>
<div>
<glue-float-field label="min" :value.sync="glue_state.size_vmin" />
</div>
<div>
<glue-float-field label="max" :value.sync="glue_state.size_vmax" />
</div>
</template>
<template v-if="glue_state.density_map">
<div>
<v-subheader class="pl-0 slider-label">dpi</v-subheader>
<glue-throttled-slider wait="300" min="12" max="144" step="1" :value.sync="dpi" hide-details />
</div>
<div>
<v-subheader class="pl-0 slider-label">contrast</v-subheader>
<glue-throttled-slider wait="300" min="0" max="1" step="0.01" :value.sync="glue_state.density_contrast"
hide-details />
</div>
</template>
<template v-else>
<div>
<v-subheader class="pl-0 slider-label">fill markers</v-subheader>
<v-switch v-model="glue_state.fill" hide-details style="margin-top: 0" />
</div>
<div>
<v-subheader class="pl-0 slider-label">show borders</v-subheader>
<v-switch v-model="glue_state.border_visible" hide-details style="margin-top: 0" />
</div>
<div>
<v-subheader class="pl-0 slider-label">border size</v-subheader>
<glue-throttled-slider wait="300" min="0" max="10" step="1" :value.sync="glue_state.border_size"
hide-details />
</div>
<div>
<v-subheader class="pl-0 slider-label">match border color to layer</v-subheader>
<v-switch v-model="glue_state.border_color_match_layer" hide-details style="margin-top: 0" />
</div>
<div>
<v-subheader class="pl-0 slider-label">border color</v-subheader>
<v-menu v-model="border_color_menu_open"
:disabled="glue_state.border_color_match_layer">
<template v-slot:activator="{ on }">
<span class="glue-color-menu"
:style="`background: ${glue_state.border_color_match_layer ? 'gray' : glue_state.border_color}`"
@click.stop="on.click"
>&nbsp;</span>
</template>
<div @click.stop="" style="text-align: end; background-color: white">
<v-btn icon @click="border_color_menu_open = false">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-color-picker v-model="glue_state.border_color"></v-color-picker>
</div>
</v-menu>
</div>
<div>
<v-subheader class="pl-0 slider-label">size scaling</v-subheader>
<glue-throttled-slider wait="300" min="0.1" max="10" step="0.01" :value.sync="glue_state.size_scaling"
hide-details />
</div>
</template>
</template>
<div class="text-subtitle-2 font-weight-bold" :style="glue_state.markers_visible ? {} : {marginTop: '6px'}">Line</div>
<div>
<v-subheader class="pl-0 slider-label">show line</v-subheader>
<v-switch v-model="glue_state.line_visible" hide-details style="margin-top: 0"/>
</div>
<template v-if="glue_state.line_visible">
<div>
<v-subheader class="pl-0 slider-label">width</v-subheader>
<glue-throttled-slider wait="300" min="1" max="20" step="1" :value.sync="glue_state.linewidth" hide-details />
</div>
<div>
<v-select label="linestyle" :items="linestyle_items" v-model="linestyle_selected" hide-details />
</div>
</template>
<div class="text-subtitle-2 font-weight-bold" :style="glue_state.markers_visible ? {} : {marginTop: '6px'}">Vectors</div>
<div>
<v-subheader class="pl-0 slider-label">show vectors</v-subheader>
<v-switch v-model="glue_state.vector_visible" hide-details style="margin-top: 0"/>
</div>
<template v-if="glue_state.vector_visible">
<div>
<v-select label="vx" :items="vx_att_items" v-model="vx_att_selected" hide-details />
</div>
<div>
<v-select label="vy" :items="vy_att_items" v-model="vy_att_selected" hide-details />
</div>
</template>
</div>
</template>
<script>
</script>
<style id="layer_scatter">
.glue-plotly-layer-scatter .v-subheader.slider-label {
font-size: 12px;
height: 16px;
margin-top: 6px;
}
</style>
10 changes: 10 additions & 0 deletions glue_plotly/viewers/scatter/state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from glue.viewers.scatter.state import DDCProperty, ScatterLayerState


class PlotlyScatterLayerState(ScatterLayerState):

border_visible = DDCProperty(False, docstring="Whether to show borders on the markers")
border_size = DDCProperty(1, docstring="The size of the marker borders")
border_color = DDCProperty(docstring="The color of the marker borders")
border_color_match_layer = DDCProperty(False, docstring="If true, border color options are ignored, "
"and the border matches the layer")
4 changes: 2 additions & 2 deletions glue_plotly/viewers/scatter/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

from glue_plotly.common.scatter2d import polar_layout_config, radial_axis, rectilinear_layout_config

from glue_jupyter.common.state_widgets.layer_scatter import ScatterLayerStateWidget
from glue_jupyter.registries import viewer_registry

from .layer_artist import PlotlyScatterLayerArtist
from .layer_state_widget import PlotlyScatterLayerStateWidget
from .viewer_state_widget import PlotlyScatterViewerStateWidget
from glue_plotly.viewers import PlotlyBaseView

Expand All @@ -33,7 +33,7 @@ class PlotlyScatterView(PlotlyBaseView):
_options_cls = PlotlyScatterViewerStateWidget
_data_artist_cls = PlotlyScatterLayerArtist
_subset_artist_cls = PlotlyScatterLayerArtist
_layer_style_widget_cls = ScatterLayerStateWidget
_layer_style_widget_cls = PlotlyScatterLayerStateWidget

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down
12 changes: 6 additions & 6 deletions glue_plotly/viewers/scatter/viewer_state_widget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
</div>
</template>

<style id="viewer_image">
.glue-viewer-scatter-plotly .v-subheader.slider-label {
font-size: 12px;
height: 16px;
margin-top: 6px;
}
<style id="viewer_scatter">
.glue-viewer-scatter-plotly .v-subheader.slider-label {
font-size: 12px;
height: 16px;
margin-top: 6px;
}
</style>
Loading