Skip to content

Commit

Permalink
add new "overlay_layer" keypress callback and improve "show_layer" ca…
Browse files Browse the repository at this point in the history
…llback
  • Loading branch information
raphaelquast committed Dec 8, 2023
1 parent 7136149 commit c05d6c3
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 10 deletions.
1 change: 1 addition & 0 deletions docs/api_callbacks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ Callbacks that can be used with ``m.cb.keypress``
:nosignatures:

switch_layer
overlay_layer
fetch_layers


Expand Down
106 changes: 98 additions & 8 deletions eomaps/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,24 +1232,70 @@ def __init__(self, *args, **kwargs):
class KeypressCallbacks:
"""Collection of callbacks that are executed if you press a key on the keyboard."""

_cb_list = ["switch_layer", "fetch_layers"]
_cb_list = ["switch_layer", "fetch_layers", "overlay_layer"]

def __init__(self, m, temp_artists):
self._temporary_artists = temp_artists
self._m = m

def switch_layer(self, layer, key="x"):
"""
Change the default layer of the map.
Set the currently visible layer of the map.
Use the keyboard events to set the default layer (e.g. the visible layer)
displayed in the plot.
Parameters
----------
layer : str or list
The layer-name to use (or a list of layer-names to combine).
For details on how to specify layer-names, see :py:meth:`Maps.show_layer`
Additional Parameters
---------------------
key : str, optional
The key to use for triggering the callback.
Modifiers are indicated with a "+", e.g. "alt+x".
The default is "x".
Examples
--------
Show layer A:
>>> m.cb.keypress.attach.overlay_layer(layer="A", key="x")
Show layer B with 50% transparency on top of layer A
>>> m.cb.keypress.attach.overlay_layer(layer="A|B{0.5}", key="x")
Show layer B on top of layer A:
>>> m.cb.keypress.attach.overlay_layer(layer=["A", "B"], key="x")
Show layer B with 50% transparency on top of layer A
>>> m.cb.keypress.attach.overlay_layer(layer=["A", ("B", 0.5)], key="x")
"""
if isinstance(layer, (list, tuple)):
self._m.show_layer(*layer)
elif isinstance(layer, str):
self._m.show_layer(layer)

def overlay_layer(self, layer, key="x"):
"""
Toggle displaying a layer on top of the currently visible layers.
- If the layer is not part of the currently visible layers, it will be
added on top.
- If the layer is part of the currently visible layers, it will be removed.
Parameters
----------
layer : str
The layer-name to use.
If a non-string value is provided, it will be converted to string!
layer : str, tuple or list
The layer-name to use, a tuple (layer, transparency) or a list of
the aforementioned types to combine.
For details on how to specify layer-names, see :py:meth:`Maps.show_layer`
Additional Parameters
---------------------
Expand All @@ -1258,8 +1304,52 @@ def switch_layer(self, layer, key="x"):
Modifiers are indicated with a "+", e.g. "alt+x".
The default is "x".
Note
----
If the visible layer changes **while the overlay-layer is active**,
triggering the callback again might not properly remove the previous overlay!
(e.g. the overlay is only removed if the top-layer corresponds exactly to
the overlay-layer specifications)
Examples
--------
Toggle overlaying layer A:
>>> m.cb.keypress.attach.overlay_layer(layer="A", key="x")
Toggle overlaying layer A with 50% transparency:
>>> m.cb.keypress.attach.overlay_layer(layer=("A", 0.5), key="x")
Toggle overlaying a combined layer (showing layer B with 50% transparency
on top of layer A)
>>> m.cb.keypress.attach.overlay_layer(layer="A|B{0.5}", key="x")
Toggle overlaying a combined layer (showing layer B on top of layer A)
>>> m.cb.keypress.attach.overlay_layer(layer=["A", "B"], key="x")
Toggle overlaying a combined layer (showing layer B with 50% transparency
on top of layer A)
>>> m.cb.keypress.attach.overlay_layer(layer=["A", ("B", 0.5)], key="x")
"""
self._m.show_layer(layer)

if isinstance(layer, list):
layer = self._m._get_combined_layer_name(*layer)
elif isinstance(layer, tuple):
# e.g. (layer-name, layer-transparency)
layer = self._m._get_combined_layer_name(layer)

# in case the layer is currently on top, remove it
if not self._m.BM.bg_layer.endswith(f"|{layer}"):
self._m.show_layer(self._m.BM.bg_layer, layer)
else:
newlayer = self._m.BM.bg_layer.removesuffix(f"|{layer}")
if len(newlayer) > 0:
self._m.show_layer(newlayer)

def fetch_layers(self, layers=None, verbose=True, key="x"):
"""
Expand Down
41 changes: 39 additions & 2 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,43 @@ def loadmethod(db, ID):
m.cb.pick.remove(cid)
plt.close("all")

def test_overlay_layer(self):
# ---------- test as CLICK callback
m = self.create_basic_map()
m_a = m.new_layer("A")
m_b = m.new_layer("B")

cid0 = m.all.cb.keypress.attach.overlay_layer(layer="A", key="0")
cid1 = m.all.cb.keypress.attach.overlay_layer(layer=("B", 0.5), key="1")
cid2 = m.all.cb.keypress.attach.overlay_layer(layer=["A", ("B", 0.5)], key="2")

init_layer = m.layer

key_press_event(m.f.canvas, "0")
key_release_event(m.f.canvas, "0")
self.assertTrue(m.BM._bg_layer == m._get_combined_layer_name(m.layer, "A"))
key_press_event(m.f.canvas, "0")
key_release_event(m.f.canvas, "0")
self.assertTrue(m.BM._bg_layer == m.layer)

key_press_event(m.f.canvas, "1")
key_release_event(m.f.canvas, "1")
self.assertTrue(
m.BM._bg_layer == m._get_combined_layer_name(m.layer, ("B", 0.5))
)
key_press_event(m.f.canvas, "1")
key_release_event(m.f.canvas, "1")
self.assertTrue(m.BM._bg_layer == m.layer)

key_press_event(m.f.canvas, "2")
key_release_event(m.f.canvas, "2")
self.assertTrue(
m.BM._bg_layer == m._get_combined_layer_name(m.layer, "A", ("B", 0.5))
)
key_press_event(m.f.canvas, "2")
key_release_event(m.f.canvas, "2")
self.assertTrue(m.BM._bg_layer == m.layer)

def test_switch_layer(self):
# ---------- test as CLICK callback
m = self.create_basic_map()
Expand All @@ -666,7 +703,7 @@ def test_switch_layer(self):
cid1 = m.all.cb.keypress.attach.switch_layer(layer="2", key="2")

# a callback only active on the "base" layer
cid3 = m.cb.keypress.attach.switch_layer(layer="3", key="3")
cid3 = m.cb.keypress.attach.switch_layer(layer=["2", ("3", 0.5)], key="3")

# switch to layer 2
key_press_event(m.f.canvas, "2")
Expand All @@ -686,7 +723,7 @@ def test_switch_layer(self):
# now the 3rd callback should trigger
key_press_event(m.f.canvas, "3")
key_release_event(m.f.canvas, "3")
self.assertTrue(m.BM._bg_layer == "3")
self.assertTrue(m.BM._bg_layer == m._get_combined_layer_name("2", ("3", 0.5)))

m.all.cb.keypress.remove(cid0)
m.all.cb.keypress.remove(cid1)
Expand Down

0 comments on commit c05d6c3

Please sign in to comment.