diff --git a/corrscope/gui/__init__.py b/corrscope/gui/__init__.py index 2c8dd5b..2262425 100644 --- a/corrscope/gui/__init__.py +++ b/corrscope/gui/__init__.py @@ -494,6 +494,9 @@ class ShortcutButton(qw.QPushButton): self.setToolTip(keys.toString(QKeySequence.NativeText)) +# Begin ConfigModel properties + + def nrow_ncol_property(altered: str, unaltered: str) -> property: def get(self: "ConfigModel"): val = getattr(self.cfg.layout, altered) @@ -541,6 +544,24 @@ def color2hex_property(path: str) -> property: return property(getter, setter) +def color2hex_maybe_property(path: str) -> property: + # TODO turn into class, and use __set_name__ to determine assignment LHS=path. + def getter(self: "ConfigModel"): + color_attr = rgetattr(self.cfg, path) + if not color_attr: + return "" + return color2hex(color_attr) + + def setter(self: "ConfigModel", val: str): + if val: + color = color2hex(val) + else: + color = None + rsetattr(self.cfg, path, color) + + return property(getter, setter) + + def path_strip_quotes(path: str) -> str: if len(path) and path[0] == path[-1] == '"': return path[1:-1] @@ -569,6 +590,7 @@ class ConfigModel(PresentationModel): render__bg_color = color2hex_property("render__bg_color") render__init_line_color = color2hex_property("render__init_line_color") + render__grid_color = color2hex_maybe_property("render__grid_color") @property def render_video_size(self) -> str: @@ -603,6 +625,9 @@ class ConfigModel(PresentationModel): render__line_width = default_property("render__line_width", 1.5) +# End ConfigModel + + class ChannelTableView(qw.QTableView): def append_channels(self, wavs: List[str]): model: ChannelModel = self.model() diff --git a/corrscope/gui/mainwindow.ui b/corrscope/gui/mainwindow.ui index 521d9c1..86e4167 100644 --- a/corrscope/gui/mainwindow.ui +++ b/corrscope/gui/mainwindow.ui @@ -167,6 +167,16 @@ + + + + Grid Color + + + + + + diff --git a/corrscope/layout.py b/corrscope/layout.py index ce4765e..2a01391 100644 --- a/corrscope/layout.py +++ b/corrscope/layout.py @@ -1,4 +1,4 @@ -from typing import Optional, TypeVar, Callable, List +from typing import Optional, TypeVar, Callable, List, Generic import numpy as np @@ -92,3 +92,11 @@ class RendererLayout: regions2d = regions2d.T return regions2d.flatten()[: self.nplots].tolist() + + +class EdgeFinder(Generic[Region]): + def __init__(self, regions2d: np.ndarray): + self.tops: List[Region] = regions2d[0, :].tolist() + self.bottoms: List[Region] = regions2d[-1, :].tolist() + self.lefts: List[Region] = regions2d[:, 0].tolist() + self.rights: List[Region] = regions2d[:, -1].tolist() diff --git a/corrscope/renderer.py b/corrscope/renderer.py index 41d4b7e..3a07f92 100644 --- a/corrscope/renderer.py +++ b/corrscope/renderer.py @@ -7,7 +7,7 @@ import numpy as np import attr from corrscope.config import register_config -from corrscope.layout import RendererLayout, LayoutConfig +from corrscope.layout import RendererLayout, LayoutConfig, EdgeFinder from corrscope.outputs import RGB_DEPTH, ByteBuffer from corrscope.util import coalesce @@ -60,6 +60,7 @@ class RendererConfig: bg_color: str = "#000000" init_line_color: str = default_color() + grid_color: Optional[str] = None @attr.dataclass @@ -137,6 +138,8 @@ class MatplotlibRenderer(Renderer): self._set_layout() # mutates self + transparent = "#00000000" + def _set_layout(self) -> None: """ Creates a flat array of Matplotlib Axes, with the new layout. @@ -152,18 +155,50 @@ class MatplotlibRenderer(Renderer): raise Exception("I don't currently expect to call _set_layout() twice") # plt.close(self.fig) + grid_color = self.cfg.grid_color axes2d: np.ndarray["Axes"] self._fig, axes2d = plt.subplots( self.layout.nrows, self.layout.ncols, squeeze=False, - # Remove gaps between Axes + # Remove axis ticks (which slow down rendering) + subplot_kw=dict(xticks=[], yticks=[]), + # Remove gaps between Axes TODO borders shouldn't be half-visible gridspec_kw=dict(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0), ) - # remove Axis from Axes - for ax in axes2d.flatten(): - ax.set_axis_off() + ax: "Axes" + if grid_color: + # Initialize borders + for ax in axes2d.flatten(): + # Hide Axises + # (drawing them is very slow, and we disable ticks+labels anyway) + ax.get_xaxis().set_visible(False) + ax.get_yaxis().set_visible(False) + + # Background color + ax.set_facecolor(self.transparent) + + # Set border colors + for spine in ax.spines.values(): + spine.set_color(grid_color) + + # gridspec_kw indexes from bottom-left corner. + # Only show bottom-left borders (x=0, y=0) + ax.spines["top"].set_visible(False) + ax.spines["right"].set_visible(False) + + # Hide bottom-left edges for speed. + edge_axes: EdgeFinder["Axes"] = EdgeFinder(axes2d) + for ax in edge_axes.bottoms: + ax.spines["bottom"].set_visible(False) + for ax in edge_axes.lefts: + ax.spines["left"].set_visible(False) + + else: + # Remove Axis from Axes + for ax in axes2d.flatten(): + ax.set_axis_off() # Generate arrangement (using nplots, cfg.orientation) self._axes = self.layout.arrange(lambda row, col: axes2d[row, col])