first commint
This commit is contained in:
305
inspect_cells.py
Normal file
305
inspect_cells.py
Normal file
@@ -0,0 +1,305 @@
|
||||
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')
|
||||
Reference in New Issue
Block a user