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

Fix missing color map inspector issue. Improve color map registry. #1216

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion nion/swift/Inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,8 @@ def __init__(self, document_controller: DocumentController.DocumentController, d
self.color_map_items.append(color_map.name)
self._color_map_flags.append(color_map_key)
self._color_map_reverse_map = {p: i for i, p in enumerate(self._color_map_flags)}
self.current_colormap_index = Model.PropertyModel[int](self._color_map_reverse_map[self._display_data_channel.color_map_id])
color_map_index = self._color_map_reverse_map.get(self._display_data_channel.color_map_id, 0)
self.current_colormap_index = Model.PropertyModel[int](color_map_index)

self.brightness_model = DisplayDataChannelPropertyCommandModel(document_controller, display_data_channel, "brightness", title=_("Change Brightness"), command_id="change_brightness")

Expand Down
70 changes: 43 additions & 27 deletions nion/swift/model/ColorMaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,38 @@ def generate_lookup_array_hsv() -> _RGBA8ImageDataType:

@dataclasses.dataclass
class ColorMap:
color_map_id: str
name: str
data: _RGBA8ImageDataType


color_maps: typing.Dict[str, ColorMap] = dict()

color_maps["grayscale"] = ColorMap(_("Grayscale"), generate_lookup_array_grayscale())
color_maps["magma"] = ColorMap(_("Magma"), generate_lookup_array('magma'))
color_maps["hsv"] = ColorMap(_("HSV"), generate_lookup_array_hsv())
color_maps["viridis"] = ColorMap(_("Viridis"), generate_lookup_array('viridis'))
color_maps["plasma"] = ColorMap(_("Plasma"), generate_lookup_array('plasma'))
color_maps["ice"] = ColorMap(_("Ice"), generate_lookup_array('ice'))
def add_color_map(color_map: ColorMap) -> None:
color_maps[color_map.color_map_id] = color_map

add_color_map(ColorMap("grayscale", _("Grayscale"), generate_lookup_array_grayscale()))
add_color_map(ColorMap("magma", _("Magma"), generate_lookup_array('magma')))
add_color_map(ColorMap("hsv", _("HSV"), generate_lookup_array_hsv()))
add_color_map(ColorMap("viridis", _("Viridis"), generate_lookup_array('viridis')))
add_color_map(ColorMap("plasma", _("Plasma"), generate_lookup_array('plasma')))
add_color_map(ColorMap("ice", _("Ice"), generate_lookup_array('ice')))


def get_color_map_id_from_name(name: str) -> str:
color_map_id = name.lower()
color_map_id = re.sub(r"[^\w\s]", '', color_map_id)
color_map_id = re.sub(r"\s+", '-', color_map_id)
return color_map_id


def load_color_map_json_str(color_map_json_str: str) -> None:
color_map_json = json.loads(color_map_json_str)
color_maps[color_map_json["id"]] = ColorMap(color_map_json["name"], generate_lookup_array_from_points(color_map_json["points"], 256))
color_map_id = color_map_json["id"]
add_color_map(ColorMap(color_map_id, color_map_json["name"], generate_lookup_array_from_points(color_map_json["points"], 256)))


def load_color_map_xml_str(color_map_xml_str: str, name: str) -> None:
def load_color_map_xml_str(color_map_xml_str: str, name: str, color_map_id: str) -> None:
tree_root = xml.etree.ElementTree.fromstring(color_map_xml_str)
assert tree_root.tag == "ColorMaps"
color_map_tree = list(tree_root)[0]
Expand All @@ -156,10 +168,7 @@ def load_color_map_xml_str(color_map_xml_str: str, name: str) -> None:
if "x" in raw_point:
points.append({"x": float(raw_point['x']), "r": float(raw_point['r']), "g": float(raw_point['g']),
"b": float(raw_point['b'])})
color_map_id = name.lower()
color_map_id = re.sub(r"[^\w\s]", '', color_map_id)
color_map_id = re.sub(r"\s+", '-', color_map_id)
color_maps[color_map_id] = ColorMap(name, generate_lookup_array_from_points(points, 256))
add_color_map(ColorMap(color_map_id, name, generate_lookup_array_from_points(points, 256)))
"""
# this section can be used to generate .json from .xml color tables
points2 = [{"x": point['x'], "rgb": [round(point['r'] * 255), round(point['g'] * 255), round(point['b'] * 255)]} for point in points]
Expand Down Expand Up @@ -195,14 +204,17 @@ def load_color_maps(color_maps_dir: pathlib.Path) -> None:
elif color_map_file_extension == ".xml":
with open(color_map_path, "r") as f:
xml_str = f.read()
load_color_map_xml_str(xml_str, color_map_path.stem)
load_color_map_xml_str(xml_str, color_map_path.stem, get_color_map_id_from_name(color_map_path.stem))
except Exception as e:
import traceback
traceback.print_exc()


def load_color_map_from_dict(d: typing.Mapping[str, typing.Any]) -> None:
color_maps[d["id"]] = ColorMap(d["name"], generate_lookup_array_from_points(d["points"], 256))
color_map_id = d["id"]
color_map_name = d["name"]
color_map_points = d["points"]
add_color_map(ColorMap(color_map_id, color_map_name, generate_lookup_array_from_points(color_map_points, 256)))


def load_color_map_resource(resource_path: str) -> None:
Expand All @@ -221,23 +233,27 @@ def get_color_map_data_by_id(color_map_id: str) -> _RGBA8ImageDataType:
return color_maps.get(color_map_id, color_maps["grayscale"]).data


class ColorMapProtocol(typing.Protocol):
color_map_id: str
name: str
data: typing.Optional[_RGBA8ImageDataType] = None
json_str: typing.Optional[str] = None
xml_str: typing.Optional[str] = None


def component_registered(component: Registry._ComponentType, component_types: typing.Set[str]) -> None:
if "color-map-dict" in component_types:
color_map_dict = typing.cast(typing.Mapping[str, typing.Any], component)
load_color_map_from_dict(color_map_dict)
elif "color-map-json-str" in component_types:
load_color_map_json_str(typing.cast(str, component))
elif "color-map-xml-str" in component_types:
color_map_xml_str, color_map_name = typing.cast(typing.Tuple[str, str], component)
load_color_map_xml_str(color_map_xml_str, color_map_name)
if "color-map-description" in component_types:
color_map_description = typing.cast(ColorMapProtocol, component)
if hasattr(color_map_description, "data") and color_map_description.data is not None:
add_color_map(ColorMap(color_map_description.color_map_id, color_map_description.name, color_map_description.data))
elif hasattr(color_map_description, "json_str") and color_map_description.json_str is not None:
load_color_map_json_str(color_map_description.json_str)
elif hasattr(color_map_description, "xml_str") and color_map_description.xml_str is not None:
load_color_map_xml_str(color_map_description.xml_str, color_map_description.name, color_map_description.color_map_id)


def component_unregistered(component: Registry._ComponentType, component_types: typing.Set[str]) -> None:
if "color-map-dict" in component_types:
pass
elif "color-map-json-str" in component_types:
pass
elif "color-map-xml-str" in component_types:
if "color-map-description" in component_types:
pass


Expand Down
11 changes: 11 additions & 0 deletions nion/swift/test/Inspector_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,17 @@ def test_image_display_inspector_sets_display_limits_when_text_is_changed(self):
inspector_section.image_data_inspector_handler.display_limits_limit_high.editing_finished("")
self.assertEqual(display_data_channel.display_limits, None)

def test_image_data_inspector_handles_missing_color_table(self) -> None:
with TestContext.create_memory_context() as test_context:
document_controller = test_context.create_document_controller()
document_model = document_controller.document_model
data_item = DataItem.DataItem(numpy.zeros((4, 4), numpy.uint32))
document_model.append_data_item(data_item)
display_item = document_model.get_display_item_for_data_item(data_item)
display_data_channel = display_item.display_data_channels[0]
display_data_channel.color_map_id = "__missing__"
Inspector.ImageDataInspectorModel(document_controller, display_data_channel, display_item)

def test_inspector_handles_deleted_data(self):
with TestContext.create_memory_context() as test_context:
document_controller = test_context.create_document_controller()
Expand Down
Loading