📢作者: 小小明-代码实体
📢博客主页:https://blog.csdn.net/as604049322
📢欢迎点赞 👍 收藏 ⭐留言 📝 欢迎讨论!
openpyxl
支持以下几种颜色类型:
- RGB (Red, Green, Blue): 这是最常用的颜色类型,允许通过指定红、绿、蓝三原色的组合来自定义颜色。RGB值通常以十六进制格式表示。
- Theme: Excel有一套主题颜色,可以通过指定主题颜色的索引来使用这些颜色。
- Indexed: 这是Excel早期版本中使用的一种颜色系统,它通过索引号来引用一组预定义的颜色。
对于RGB类型的颜色直接使用cell.fill.start_color.rgb即可获取其颜色,但是对于Theme类型的单元格获取颜色却返回一个错误。
这是因为主题色会随着主题的变化而变化,如下图:
可以看到每个主题有10个基础色,然后受到透明度的影响,我将第一个单元格设置了上图的主题色。
我们创建Excel文件进行测试:
from openpyxl import load_workbook
wb = load_workbook("color_test.xlsx")
wbs = wb.active
cell = wbs.cell(1, 1)
cell.fill.start_color
<openpyxl.styles.colors.Color object>
Parameters:
rgb=None, indexed=None, auto=None, theme=5, tint=0.4, type='theme'
可以看到这就是一个theme类型的颜色,确实是索引5的位置,透明度40%。
我们尝试获取rgb颜色:
cell.fill.start_color.rgb
会返回Values must be of type <class 'str'>
这样一个带有错误信息的字符串。
当然也可以调用index属性自动获取rgb或者在主题色中的索引:
cell.fill.start_color.index
5
如果是一个RGB 类型的颜色:
cell = wbs.cell(1, 3)
cell.fill.start_color
<openpyxl.styles.colors.Color object>
Parameters:
rgb='FFF4B382', indexed=None, auto=None, theme=None, tint=0.0, type='rgb'
此时调用index或rgb属性都可以获取rgb颜色值。
那么我们如何获取主题色对应的RGB颜色呢?这个openpyxl并没有提供一个直接的方式,我们只能自己做xml解析了。下面我封装了一个工具类:
from colorsys import rgb_to_hls, hls_to_rgb
class ThemeColorConverter:
RGBMAX = 0xff
HLSMAX = 240
def __init__(self, wb):
self.colors = self.get_theme_colors(wb)
@staticmethod
def tint_luminance(tint, lum):
if tint < 0:
return int(round(lum * (1.0 + tint)))
return int(round((ThemeColorConverter.HLSMAX - lum) * tint)) + lum
@staticmethod
def ms_hls_to_rgb(hue, lightness=None, saturation=None):
if lightness is None:
hue, lightness, saturation = hue
hlsmax = ThemeColorConverter.HLSMAX
return hls_to_rgb(hue / hlsmax, lightness / hlsmax, saturation / hlsmax)
@staticmethod
def rgb_to_hex(red, green=None, blue=None):
if green is None:
red, green, blue = red
return '{:02X}{:02X}{:02X}'.format(
int(red * ThemeColorConverter.RGBMAX),
int(green * ThemeColorConverter.RGBMAX),
int(blue * ThemeColorConverter.RGBMAX)
)
@staticmethod
def rgb_to_ms_hls(red, green=None, blue=None):
if green is None:
if isinstance(red, str):
if len(red) > 6:
red = red[-6:] # Ignore preceding '#' and alpha values
rgbmax = ThemeColorConverter.RGBMAX
blue = int(red[4:], 16) / rgbmax
green = int(red[2:4], 16) / rgbmax
red = int(red[0:2], 16) / rgbmax
else:
red, green, blue = red
h, l, s = rgb_to_hls(red, green, blue)
hlsmax = ThemeColorConverter.HLSMAX
return (int(round(h * hlsmax)), int(round(l * hlsmax)),
int(round(s * hlsmax)))
@staticmethod
def get_theme_colors(wb):
from openpyxl.xml.functions import QName, fromstring
xlmns = 'http://schemas.openxmlformats.org/drawingml/2006/main'
root = fromstring(wb.loaded_theme)
themeEl = root.find(QName(xlmns, 'themeElements').text)
colorSchemes = themeEl.findall(QName(xlmns, 'clrScheme').text)
firstColorScheme = colorSchemes[0]
colors = []
for c in ['lt1', 'dk1', 'lt2', 'dk2', 'accent1', 'accent2', 'accent3', 'accent4', 'accent5', 'accent6']:
accent = firstColorScheme.find(QName(xlmns, c).text)
for i in list(accent):
if 'window' in i.attrib['val']:
colors.append(i.attrib['lastClr'])
else:
colors.append(i.attrib['val'])
return colors
def theme_and_tint_to_rgb(self, theme, tint):
rgb = self.colors[theme]
h, l, s = self.rgb_to_ms_hls(rgb)
return self.rgb_to_hex(self.ms_hls_to_rgb(h, self.tint_luminance(tint, l), s))
具体如何xml解析可以看上面的get_theme_colors函数。
下面我们获取一下当前Excel选中主题的10个基础色调:
theme_color = ThemeColorConverter(wb)
print(theme_color.colors)
['FFFFFF', '000000', 'E7E6E6', '44546A', '4874CB', 'EE822F', 'F2BA02', '75BD42', '30C0B4', 'E54C5E']
然后我们传入索引和透明度获取颜色:
theme_color.theme_and_tint_to_rgb(5, 0.4)
'F4B281'
但使用取色工具测量第一个单元格的颜色值为#f4b382,有微量误差,这属于正常现象,也完全不会影响视觉。这是因为hls+透明度转rgb颜色的过程中存在小数运算,四舍五入后就会造成一定误差。
人眼对低位数据变化不敏感
在24位位图中高8位构成蓝色通道,中8位构成绿色通道,低8位构成红色通道。经测试,在删除各通道的低4位数据并加入随机噪音后,图片用肉眼无法观察到任何变化。现在展示一张图片在删除各通道的低位数据,并加入随机噪音后图片的变化。
可以看到在每个通道低三位的数据进行随意修改,肉眼几乎看不出变化。低三位,意味着7以内的变化都不会有影响。
最后我们封装一个可以查看任何单元格颜色的函数:
from openpyxl.styles.colors import COLOR_INDEX
def get_cell_color(cell):
color = cell.fill.start_color
if color.type == "rgb":
return color.rgb
elif color.type == "indexed":
color_index = color.indexed
if color_index is None or color_index < len(COLOR_INDEX):
raise Exception("Invalid indexed color")
return COLOR_INDEX[color_index]
elif color.type == "theme":
return "FF" + theme_color.theme_and_tint_to_rgb(color.theme, color.tint)
else:
raise Exception(f"Other type: {color.type}")
下面我们将这些主题色都测一测:
theme_color = ThemeColorConverter(wb)
wbs = wb.active
for r in range(1, 4):
colors = [get_cell_color(wbs.cell(r, c)) for c in range(1, 8)]
print(f"第{r}行的单元格的颜色为", colors)
第1行的单元格的颜色为 ['FFFFFFFF', 'FF000000', 'FFE7E6E6', 'FF44546A', 'FF4772CA', 'FFEE802E', 'FFF2BC02']
第2行的单元格的颜色为 ['FFF2F2F2', 'FF7F7F7F', 'FFD0CECE', 'FFD5DBE4', 'FFDAE3F4', 'FFFBE5D5', 'FFFEF2CA']
第3行的单元格的颜色为 ['FFBFBFBF', 'FF3F3F3F', 'FF757070', 'FF8496AF', 'FF90A9DF', 'FFF4B281', 'FFFDDA60']
可以看到,全部获取到低位误差小于1的RGB颜色。
附录
openpyxl获取的颜色值由四组16进制数表示,分别是:
- Alpha(透明度):范围也是
00
到FF
,其中FF
表示完全不透明,而00
表示完全透明。 - 红色(Red)
- 绿色(Green)
- 蓝色(Blue)
不过在wps中测试,透明度不起任何效果,不排除office或WPS未来版本支持ARGB的颜色,但目前WPS对A通道的透明度值会直接忽略。
本文链接:https://blog.csdn.net/as604049322/article/details/134470419