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])