Files
mapTools/inspect_cells.py
2025-12-06 17:48:48 +08:00

306 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import openpyxl
import sys
try:
from openpyxl.styles.colors import COLOR_INDEX
except Exception:
COLOR_INDEX = []
FILE = 'global.xlsx'
SHEET = 'Sheet1'
CELLS = ['P547', 'X458']
try:
wb = openpyxl.load_workbook(FILE, data_only=False)
except Exception as e:
print(f"Failed to open {FILE}: {e}")
sys.exit(1)
if SHEET in wb.sheetnames:
ws = wb[SHEET]
else:
print(f"Sheet '{SHEET}' not found. Available sheets: {wb.sheetnames}. Using active sheet '{wb.active.title}'.")
ws = wb.active
def color_info(c):
if c is None:
return None
info = {}
for attr in ('type','rgb','indexed','index','theme','tint','auto'):
try:
info[attr] = getattr(c, attr, None)
except Exception as e:
info[attr] = f"<error: {e}>"
# 归一化尝试:把常见的 tuple/list/int/bytes 等 rgb 表示转换为可读字符串
info['normalized'], info['original_repr'] = normalize_color_value(c)
try:
kind, value = safe_resolve_color(c)
info['resolved'] = {'kind': kind, 'value': value}
except Exception as e:
info['resolved'] = {'error': str(e)}
return info
def normalize_color_value(val):
"""
尝试把颜色对象或多种表示归一化为一个字符串(优先 ARGB hex并返回 (normalized_str_or_None, original_repr).
支持:
- openpyxl Color 对象 (会尝试读取 .rgb/.indexed/.index/.theme/.tint)
- str (如 'FF00B050', '#FF00B050', 'rgb(0,176,80)')
- tuple/list of ints (r,g,b) 或 (r,g,b,a)
- int (如 16711680)
- bytes
任何不能解析的值将返回 (None, repr(val)).
"""
import logging
logger = logging.getLogger(__name__)
def as_argb_from_rgb_tuple(tpl):
try:
comps = list(tpl)
if len(comps) == 3:
r, g, b = comps
a = 255
elif len(comps) >= 4:
r, g, b, a = comps[:4]
else:
return None
return "{0:02X}{1:02X}{2:02X}{3:02X}".format(a, r, g, b)
except Exception as e:
logger.debug(f"as_argb_from_rgb_tuple failed: {e} for {tpl}")
return None
# If it's an openpyxl Color-like object, try attributes first
try:
# guard: if object has .rgb/.indexed etc, prefer those
if hasattr(val, 'rgb') or hasattr(val, 'indexed') or hasattr(val, 'index') or hasattr(val, 'theme'):
# try .rgb
try:
rgb = getattr(val, 'rgb', None)
if rgb:
s = str(rgb)
# strip leading '#' if present
if s.startswith('#'):
s = s[1:]
if len(s) == 6:
s = 'FF' + s
return (s.upper(), repr(rgb))
except Exception as e:
logger.debug(f"error reading .rgb: {e}")
# try indexed/index mapping (best-effort)
try:
idx = getattr(val, 'indexed', None) or getattr(val, 'index', None)
if idx is not None:
try:
idx_int = int(idx)
# map using openpyxl COLOR_INDEX if available
if COLOR_INDEX and 0 <= idx_int < len(COLOR_INDEX):
hexv = COLOR_INDEX[idx_int]
if hexv:
hexs = hexv.upper()
if len(hexs) == 6:
hexs = 'FF' + hexs
return (hexs, repr(idx_int))
return (f'INDEXED_{idx_int}', repr(idx_int))
except Exception:
return (f'INDEXED_{idx}', repr(idx))
except Exception as e:
logger.debug(f"error reading .indexed/index: {e}")
# theme
try:
theme = getattr(val, 'theme', None)
if theme is not None:
return (f'THEME_{theme}', repr(theme))
except Exception as e:
logger.debug(f"error reading .theme: {e}")
except Exception:
# not an object we can introspect
pass
# Primitive types
# str
try:
if isinstance(val, str):
s = val.strip()
if s.startswith('#'):
s = s[1:]
# If it contains 'rgb(' leave as-is
if s.lower().startswith('rgb('):
return (s, repr(val))
# if hex-like
hexchars = ''.join(ch for ch in s if ch.isalnum())
if all(c in '0123456789ABCDEFabcdef' for c in hexchars) and len(hexchars) in (6,8):
if len(hexchars) == 6:
hexchars = 'FF' + hexchars
return (hexchars.upper(), repr(val))
except Exception as e:
logger.debug(f"error normalizing str val: {e}")
# tuple/list
try:
if isinstance(val, (tuple, list)):
# might be like (r,g,b) or (r,g,b,a)
normalized = as_argb_from_rgb_tuple(val)
if normalized:
return (normalized, repr(val))
except Exception as e:
logger.debug(f"error normalizing sequence val: {e}")
# int (maybe packed RGB)
try:
if isinstance(val, int):
v = val & 0xFFFFFFFF
# If it's 24-bit or 32-bit
if v <= 0xFFFFFF:
# assume RGB
r = (v >> 16) & 0xFF
g = (v >> 8) & 0xFF
b = v & 0xFF
a = 255
else:
a = (v >> 24) & 0xFF
r = (v >> 16) & 0xFF
g = (v >> 8) & 0xFF
b = v & 0xFF
return ("{0:02X}{1:02X}{2:02X}{3:02X}".format(a, r, g, b), repr(val))
except Exception as e:
logger.debug(f"error normalizing int val: {e}")
# bytes
try:
if isinstance(val, (bytes, bytearray)):
hexs = val.hex().upper()
if len(hexs) == 6:
hexs = 'FF' + hexs
return (hexs, repr(val))
except Exception as e:
logger.debug(f"error normalizing bytes val: {e}")
# fallback: try to stringify, but filter out known library error messages
try:
r = repr(val)
if 'MUST BE OF TYPE' in r.upper() or 'VALUES MUST' in r.upper():
return (None, r)
return (r, r)
except Exception:
return (None, '<unrepresentable>')
from openpyxl.styles import colors as _colors
def safe_resolve_color(fg):
# 返回 (kind, value)
# - ('argb', 'FF00B050') 或 ('indexed', 35) 或 ('theme', 2) 或 (None, None)
try:
if fg is None:
return (None, None)
except Exception:
return (None, None)
# 1) rgb 字符串
try:
rgb = getattr(fg, 'rgb', None)
if rgb:
s = str(rgb)
if s.startswith('#'):
s = s[1:]
if len(s) == 6:
s = 'FF' + s
return ('argb', s.upper())
except Exception:
# 读取 rgb 可能抛异常,记录并继续回退
pass
# 2) indexed / index -> 尝试映射到 hex
try:
idx = getattr(fg, 'indexed', None) or getattr(fg, 'index', None)
if idx is not None:
try:
idx_int = int(idx)
# COLOR_INDEX 是 openpyxl 内置的索引颜色表
hexv = None
try:
hexv = _colors.COLOR_INDEX[idx_int] # e.g. '00FF00'
except Exception:
hexv = None
if hexv:
hexs = hexv.upper()
if len(hexs) == 6:
hexs = 'FF' + hexs
return ('argb', hexs)
else:
return ('indexed', idx_int)
except Exception:
return ('indexed', idx)
except Exception:
pass
# 3) theme
try:
theme = getattr(fg, 'theme', None)
if theme is not None:
return ('theme', theme)
except Exception:
pass
return (None, None)
for coord in CELLS:
print(f"--- {coord} ---")
cell = ws[coord]
print('coordinate:', cell.coordinate)
print('row, column:', cell.row, cell.column)
print('value repr:', repr(cell.value))
print('data_type:', getattr(cell, 'data_type', None))
print('is_date:', getattr(cell, 'is_date', None))
print('number_format:', getattr(cell, 'number_format', None))
print('font:', cell.font)
try:
font_attrs = { 'name': cell.font.name, 'size': cell.font.sz, 'bold': cell.font.bold, 'italic': cell.font.italic, 'color': getattr(cell.font, 'color', None) }
except Exception as e:
font_attrs = {'error': str(e)}
print('font attributes:', font_attrs)
fill = getattr(cell, 'fill', None)
print('fill obj:', type(fill))
if fill is not None:
try:
print(' patternType:', getattr(fill, 'patternType', None))
except Exception as e:
print(' patternType error:', e)
fg = getattr(fill, 'fgColor', None)
bg = getattr(fill, 'bgColor', None)
print(' fgColor:', color_info(fg))
print(' bgColor:', color_info(bg))
fg_kind, fg_value = safe_resolve_color(fg)
bg_kind, bg_value = safe_resolve_color(bg)
print(' fgColor resolved:', {'kind': fg_kind, 'value': fg_value})
print(' bgColor resolved:', {'kind': bg_kind, 'value': bg_value})
else:
print(' fill is None')
print('alignment:', cell.alignment)
print('border:', cell.border)
print('protection:', cell.protection)
try:
comment = cell.comment.text if cell.comment else None
except Exception as e:
comment = f'<error reading comment: {e}>'
print('comment:', comment)
try:
hyperlink = cell.hyperlink.target if cell.hyperlink else None
except Exception as e:
hyperlink = f'<error reading hyperlink: {e}>'
print('hyperlink:', hyperlink)
# raw repr of the cell object's __dict__ if available
try:
d = {k: repr(v) for k,v in cell.__dict__.items()}
print('cell.__dict__ keys:', list(cell.__dict__.keys()))
except Exception:
d = None
# don't print full dict to avoid huge output, but show if present
print()
print('Done')