theme_manager.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. """
  2. Multi-theme color and visual style manager for the universal data report generator.
  3. """
  4. from pptx.dml.color import RGBColor
  5. from report_config import ThemeConfig, ThemePreset
  6. PRESETS = {
  7. ThemePreset.BUSINESS_CLASSIC: ThemeConfig(
  8. preset=ThemePreset.BUSINESS_CLASSIC,
  9. name='商务经典',
  10. primary='#1E3A5F',
  11. accent='#10B981',
  12. accent_neg='#EF4444',
  13. secondary='#64748B',
  14. dark='#1F3A5C',
  15. white='#FFFFFF',
  16. gray_bg='#F2F2F2',
  17. card_bg='#E7F0F7',
  18. text='#333333',
  19. text_gray='#666666',
  20. line='#D9D9D9',
  21. chart_series=[
  22. '#1E3A5F', '#10B981', '#ED7D31', '#64748B',
  23. '#EF4444', '#707070', '#4472C4', '#5B9BD5',
  24. ],
  25. ),
  26. ThemePreset.FRESH_SIMPLE: ThemeConfig(
  27. preset=ThemePreset.FRESH_SIMPLE,
  28. name='清新简约',
  29. primary='#1B8A5E',
  30. accent='#10B981',
  31. accent_neg='#EF4444',
  32. secondary='#94A3B8',
  33. dark='#0F5C3B',
  34. white='#FFFFFF',
  35. gray_bg='#F8FAFC',
  36. card_bg='#ECFDF5',
  37. text='#1E293B',
  38. text_gray='#64748B',
  39. line='#E2E8F0',
  40. chart_series=[
  41. '#1B8A5E', '#3B82F6', '#F59E0B', '#94A3B8',
  42. '#EF4444', '#8B5CF6', '#06B6D4', '#10B981',
  43. ],
  44. ),
  45. ThemePreset.DARK_PROFESSIONAL: ThemeConfig(
  46. preset=ThemePreset.DARK_PROFESSIONAL,
  47. name='深色专业',
  48. primary='#1E293B',
  49. accent='#38BDF8',
  50. accent_neg='#F87171',
  51. secondary='#94A3B8',
  52. dark='#0F172A',
  53. white='#FFFFFF',
  54. gray_bg='#F1F5F9',
  55. card_bg='#E2E8F0',
  56. text='#1E293B',
  57. text_gray='#475569',
  58. line='#CBD5E1',
  59. chart_series=[
  60. '#1E293B', '#38BDF8', '#F59E0B', '#94A3B8',
  61. '#F87171', '#A78BFA', '#34D399', '#FB923C',
  62. ],
  63. ),
  64. ThemePreset.WARM_BRAND: ThemeConfig(
  65. preset=ThemePreset.WARM_BRAND,
  66. name='温暖品牌',
  67. primary='#C2410C',
  68. accent='#F97316',
  69. accent_neg='#DC2626',
  70. secondary='#78716C',
  71. dark='#7C2D12',
  72. white='#FFFFFF',
  73. gray_bg='#FFFBEB',
  74. card_bg='#FFF7ED',
  75. text='#292524',
  76. text_gray='#78716C',
  77. line='#D6D3D1',
  78. chart_series=[
  79. '#C2410C', '#F97316', '#EAB308', '#78716C',
  80. '#DC2626', '#84CC16', '#06B6D4', '#A855F7',
  81. ],
  82. ),
  83. }
  84. def get_theme(preset: ThemePreset, custom_overrides: dict = None) -> ThemeConfig:
  85. if preset == ThemePreset.CUSTOM:
  86. config = ThemeConfig(preset=ThemePreset.CUSTOM, name='自定义主题')
  87. if custom_overrides:
  88. for k, v in custom_overrides.items():
  89. if hasattr(config, k):
  90. setattr(config, k, v)
  91. return config
  92. return PRESETS.get(preset, PRESETS[ThemePreset.BUSINESS_CLASSIC])
  93. def theme_to_rgb_colors(theme: ThemeConfig) -> dict:
  94. return {
  95. 'primary': _hex_to_rgb(theme.primary),
  96. 'accent': _hex_to_rgb(theme.accent),
  97. 'accent_neg': _hex_to_rgb(theme.accent_neg),
  98. 'secondary': _hex_to_rgb(theme.secondary),
  99. 'dark': _hex_to_rgb(theme.dark),
  100. 'white': _hex_to_rgb(theme.white),
  101. 'gray_bg': _hex_to_rgb(theme.gray_bg),
  102. 'card_bg': _hex_to_rgb(theme.card_bg),
  103. 'text': _hex_to_rgb(theme.text),
  104. 'text_gray': _hex_to_rgb(theme.text_gray),
  105. 'line': _hex_to_rgb(theme.line),
  106. 'green': _hex_to_rgb(theme.accent),
  107. 'red': _hex_to_rgb(theme.accent_neg),
  108. 'orange': _hex_to_rgb(theme.chart_series[2]) if len(theme.chart_series) > 2 else RGBColor(0xED, 0x7D, 0x31),
  109. 'series': [_hex_to_rgb(c) for c in theme.chart_series],
  110. }
  111. def _hex_to_rgb(hex_str: str) -> RGBColor:
  112. hex_str = hex_str.lstrip('#')
  113. if len(hex_str) == 6:
  114. return RGBColor(int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16))
  115. return RGBColor(0x33, 0x33, 0x33)
  116. def list_themes() -> list[dict]:
  117. result = []
  118. for preset, config in PRESETS.items():
  119. result.append({
  120. 'key': preset.value,
  121. 'name': config.name,
  122. 'primary': config.primary,
  123. 'accent': config.accent,
  124. })
  125. result.append({
  126. 'key': 'custom',
  127. 'name': '自定义主题',
  128. 'primary': '自定义',
  129. 'accent': '自定义',
  130. })
  131. return result
  132. if __name__ == '__main__':
  133. for t in list_themes():
  134. print(f"{t['key']}: {t['name']} (primary={t['primary']}, accent={t['accent']})")