Skip to content

Commit 23bc1d3

Browse files
committed
rectangular colormap with optional margins
1 parent d778319 commit 23bc1d3

3 files changed

Lines changed: 89 additions & 14 deletions

File tree

plotpy/mathutils/colormaps.py

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
import json
1212
import os
13-
from typing import Dict, Sequence
13+
from typing import Dict, Literal, Sequence
1414

1515
import numpy as np
16+
import qtpy.QtCore as QC
1617
import qtpy.QtGui as QG
1718
from guidata.configtools import get_module_data_path
1819
from qwt import QwtInterval, toQImage
@@ -23,6 +24,8 @@
2324
# from guidata.dataset.datatypes import NoDefault
2425
FULLRANGE = QwtInterval(0.0, 1.0)
2526
DEFAULT = EditableColormap(name="jet")
27+
SQUARE_ICON_SIZE = 16
28+
RECT_ICON_SIZE_W, RECT_ICON_SIZE_H = 80, 16
2629

2730
CmapDictType = Dict[str, EditableColormap]
2831

@@ -82,34 +85,85 @@ def save_colormaps(json_filename: str, colormaps: CmapDictType):
8285

8386

8487
def build_icon_from_cmap(
85-
cmap: EditableColormap, width: int = 16, height: int = 16
88+
cmap: EditableColormap,
89+
width: int = SQUARE_ICON_SIZE,
90+
height: int = SQUARE_ICON_SIZE,
91+
orientation: Literal["h", "v"] = "v",
92+
margin: int = 0,
8693
) -> QG.QIcon:
8794
"""Builds an icon representing the colormap
8895
8996
Args:
9097
cmap: colormap
9198
width: icon width
9299
height: icon height
100+
orientation: orientation of the colormap in the icon. Can be "h" for horizontal
101+
or "v" for vertical
102+
margin: margin around the colormap in the icon. Beware that the margin is
103+
included in the given icon size. For example, if margin is 1 and width is 16,
104+
the actual colormap width will be 14 (16 - 2 * 1). This was done to prevent
105+
interpolation on display.
93106
"""
94-
data = np.zeros((width, height), np.uint8)
95-
line = np.linspace(0, 255, width)
96-
data[:, :] = line[:, np.newaxis]
107+
108+
padded_width = width - 2 * margin
109+
padded_height = height - 2 * margin
110+
111+
data = np.zeros((padded_height, padded_width), np.uint8)
112+
113+
if orientation == "h":
114+
line = np.linspace(0, 255, padded_width)
115+
data[:, :] = line[:]
116+
else:
117+
line = np.linspace(0, 255, padded_height)
118+
data[:, :] = line[:, np.newaxis]
119+
97120
img = toQImage(data)
98121
img.setColorTable(cmap.colorTable(FULLRANGE))
99-
return QG.QIcon(QG.QPixmap.fromImage(img))
122+
cm_pxmap = QG.QPixmap.fromImage(img)
123+
124+
if margin == 0:
125+
return QG.QIcon(cm_pxmap)
126+
127+
padded_pixmap = QG.QPixmap(width, height)
128+
padded_pixmap.fill(QC.Qt.GlobalColor.transparent)
129+
130+
# Create a painter to draw onto the new pixmap
131+
painter = QG.QPainter(padded_pixmap)
132+
painter.drawPixmap(margin, margin, cm_pxmap)
133+
painter.end()
100134

135+
icon = QG.QIcon(padded_pixmap)
101136

102-
def build_icon_from_cmap_name(cmap_name: str) -> QG.QIcon:
137+
return icon
138+
139+
140+
def build_icon_from_cmap_name(
141+
cmap_name: str,
142+
width: int = SQUARE_ICON_SIZE,
143+
height: int = SQUARE_ICON_SIZE,
144+
orientation: Literal["h", "v"] = "v",
145+
margin: int = 0,
146+
) -> QG.QIcon:
103147
"""Builds an QIcon representing the colormap from the colormap name found in
104148
ALL_COLORMAPS global variable.
105149
106150
Args:
107151
cmap_name: colormap name to search in ALL_COLORMAPS
152+
width: icon width
153+
height: icon height
154+
orientation: orientation of the colormap in the icon. Can be "h" for horizontal
155+
or "v" for vertical
156+
margin: margin around the colormap in the icon. Beware that the margin is
157+
included in the given icon size. For example, if margin is 1 and width is 16,
158+
the actual colormap width will be 14 (16 - 2 * 1). This was done to prevent
159+
interpolation on display.
108160
109161
Returns:
110162
QIcon representing the colormap
111163
"""
112-
return build_icon_from_cmap(get_cmap(cmap_name.lower()))
164+
return build_icon_from_cmap(
165+
get_cmap(cmap_name.lower()), width, height, orientation, margin
166+
)
113167

114168

115169
def get_cmap(cmap_name: str) -> EditableColormap:

plotpy/styles/image.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
from plotpy._scaler import INTERP_AA, INTERP_LINEAR, INTERP_NEAREST
2424
from plotpy.config import _
2525
from plotpy.constants import LUTAlpha
26-
from plotpy.mathutils.colormaps import ALL_COLORMAPS, build_icon_from_cmap_name
26+
from plotpy.mathutils.colormaps import (
27+
ALL_COLORMAPS,
28+
RECT_ICON_SIZE_H,
29+
RECT_ICON_SIZE_W,
30+
build_icon_from_cmap_name,
31+
)
2732
from plotpy.styles.base import ItemParameters
2833

2934
if TYPE_CHECKING:
@@ -36,7 +41,15 @@ def _create_choices(
3641
"""Create the list of choices for the colormap item."""
3742
choices: list[tuple[str, str, Callable[[str], QG.QIcon]]] = []
3843
for cmap in ALL_COLORMAPS.values():
39-
choices.append((cmap.name, cmap.name, build_icon_from_cmap_name))
44+
choices.append(
45+
(
46+
cmap.name,
47+
cmap.name,
48+
lambda name: build_icon_from_cmap_name(
49+
name, RECT_ICON_SIZE_W, RECT_ICON_SIZE_H, "h", 1
50+
),
51+
)
52+
)
4053
return choices
4154

4255

@@ -59,8 +72,10 @@ class BaseImageParam(DataSet):
5972
_("Global alpha"), default=1.0, min=0, max=1, help=_("Global alpha value")
6073
)
6174
_hide_colormap = False
62-
colormap = ImageChoiceItem(_("Colormap"), _create_choices, default="jet").set_prop(
63-
"display", hide=GetAttrProp("_hide_colormap")
75+
colormap = (
76+
ImageChoiceItem(_("Colormap"), _create_choices, default="jet")
77+
.set_prop("display", hide=GetAttrProp("_hide_colormap"))
78+
.set_prop("display", size=(RECT_ICON_SIZE_W, RECT_ICON_SIZE_H))
6479
)
6580

6681
interpolation = ChoiceItem(

plotpy/widgets/colormap/manager.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
ALL_COLORMAPS,
2424
CUSTOM_COLORMAPS,
2525
DEFAULT_COLORMAPS,
26+
RECT_ICON_SIZE_H,
27+
RECT_ICON_SIZE_W,
2628
add_cmap,
2729
build_icon_from_cmap,
2830
cmap_exists,
@@ -119,14 +121,18 @@ def __init__(
119121
self._cmap_choice = QW.QComboBox()
120122
self._cmap_choice.setMaxVisibleItems(15)
121123
for cmap in ALL_COLORMAPS.values():
122-
icon = build_icon_from_cmap(cmap)
124+
icon = build_icon_from_cmap(
125+
cmap, RECT_ICON_SIZE_W, RECT_ICON_SIZE_H, "h", 1
126+
)
123127
self._cmap_choice.addItem(icon, cmap.name, cmap)
128+
129+
self._cmap_choice.setIconSize(QC.QSize(RECT_ICON_SIZE_W, RECT_ICON_SIZE_H))
124130
self._cmap_choice.setCurrentText(active_colormap)
125131
select_gbox = QW.QGroupBox(_("Select or create a colormap"))
126-
select_gbox_layout = QW.QHBoxLayout()
127132
select_label = QW.QLabel(_("Colormap presets:"))
128133
new_btn = QW.QPushButton(_("Create new colormap") + "...")
129134
new_btn.clicked.connect(self.new_colormap)
135+
select_gbox_layout = QW.QHBoxLayout()
130136
select_gbox_layout.addWidget(select_label)
131137
select_gbox_layout.addWidget(self._cmap_choice)
132138
select_gbox_layout.addSpacing(10)

0 commit comments

Comments
 (0)