Skip to content
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
39 changes: 0 additions & 39 deletions robot_log_visualizer/plotter/color_palette.py

This file was deleted.

5 changes: 3 additions & 2 deletions robot_log_visualizer/plotter/pyqtgraph_viewer_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import pyqtgraph as pg # type: ignore
from qtpy import QtCore, QtWidgets # type: ignore

from robot_log_visualizer.plotter.color_palette import ColorPalette
from robot_log_visualizer.utils.utils import ColorPalette
from robot_log_visualizer.signal_provider.signal_provider import ProviderType

# ------------------------------------------------------------------------
Expand Down Expand Up @@ -190,7 +190,8 @@ def _add_missing_curves(
y = data["data"][:]

x = data["timestamps"] - self._signal_provider.initial_time
pen = pg.mkPen(next(self._palette), width=2)
palette_color = next(self._palette)
pen = pg.mkPen(palette_color.as_hex(), width=2)
Comment on lines +193 to +194
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The code now calls next(self._palette) and then palette_color.as_hex() separately. Consider using next(self._palette).as_hex() directly to avoid storing the intermediate Color object if it's not used elsewhere.

Suggested change
palette_color = next(self._palette)
pen = pg.mkPen(palette_color.as_hex(), width=2)
pen = pg.mkPen(next(self._palette).as_hex(), width=2)

Copilot uses AI. Check for mistakes.

self._curves[key] = self._plot.plot(
x,
Expand Down
87 changes: 56 additions & 31 deletions robot_log_visualizer/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# This software may be modified and distributed under the terms of the
# Released under the terms of the BSD 3-Clause License

from __future__ import annotations

from enum import Enum


Expand All @@ -19,63 +21,86 @@ def __init__(self):


class Color:
"""
Color class to handle color in different formats
"""
"""Utility class representing a colour with handy conversions."""

def __init__(self, hex="#000000"):
def __init__(self, hex: str = "#000000"):
self.hex = hex

def as_hex(self):
def as_hex(self) -> str:
return self.hex

def as_rgb(self):
def as_rgb(self) -> tuple[int, int, int]:
return self.hex_to_rgb(self.hex)

def as_normalized_rgb(self):
def as_normalized_rgb(self) -> tuple[float, float, float]:
return self.get_to_normalized_rgb(self.hex)

@staticmethod
def hex_to_rgb(hex):
def hex_to_rgb(hex_value: str) -> tuple[int, int, int]:
# https://stackoverflow.com/questions/29643352/converting-hex-to-rgb-value-in-python
hex = hex.lstrip("#")
hlen = len(hex)
return tuple(int(hex[i : i + hlen // 3], 16) for i in range(0, hlen, hlen // 3))
hex_value = hex_value.lstrip("#")
hlen = len(hex_value)
return tuple(
int(hex_value[i : i + hlen // 3], 16)
for i in range(0, hlen, hlen // 3)
)

@staticmethod
def get_to_normalized_rgb(hex):
rgb = Color.hex_to_rgb(hex)
def get_to_normalized_rgb(hex_value: str) -> tuple[float, float, float]:
rgb = Color.hex_to_rgb(hex_value)
return (rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0)


DEFAULT_COLOR_CYCLE = (
"#1f77b4", # blue
"#ff7f0e", # orange
"#2ca02c", # green
"#d62728", # red
"#9467bd", # purple
"#8c564b", # brown
"#e377c2", # pink
"#7f7f7f", # gray
"#bcbd22", # olive
"#17becf", # cyan
)


class ColorPalette:
"""
Color palette class to handle color palette.
The user can get a color from the palette and the palette will automatically
cycle through the colors.
"""Cycling palette yielding :class:`Color` objects.

Args:
colors: Optional iterable of colour specifications (hex strings or
:class:`Color` instances). When omitted, ``DEFAULT_COLOR_CYCLE`` is
used.
"""

def __init__(self):
# use matlab color palette
def __init__(self, colors=None):
palette = colors or DEFAULT_COLOR_CYCLE

self._color_palette = [
Color("#0072BD"),
Color("#D95319"),
Color("#EDB120"),
Color("#7E2F8E"),
Color("#77AC30"),
Color("#4DBEEE"),
Color("#A2142F"),
Color("#7E2F8E"),
Color("#77AC30"),
Color("#4DBEEE"),
Color("#A2142F"),
color if isinstance(color, Color) else Color(str(color))
for color in palette
]

if not self._color_palette:
raise ValueError("ColorPalette requires at least one colour")

self._index = 0

def __iter__(self):
self._index = 0
return self

def __next__(self):
def __next__(self) -> Color:
color = self._color_palette[self._index]
self._index = (self._index + 1) % len(self._color_palette)
return color

def __len__(self) -> int:
return len(self._color_palette)

def __call__(self, index: int) -> Color:
return self._color_palette[index % len(self._color_palette)]

def reset(self) -> None:
self._index = 0
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ install_requires =
PySide2
qtpy
pyqtconsole
matplotlib
h5py
pyqtgraph
include_package_data = True
Expand Down