# TUI概念与设计
## 🎯 学习目标
通过本章学习,您将能够:
- 理解TUI(文本用户界面)的概念和优势
- 掌握现代TUI设计的原理和最佳实践
- 学会使用Python Textual框架构建交互式界面
- 在Chat-Room项目中设计优雅的终端用户界面
## 🖥️ TUI设计理念
### TUI vs GUI 对比分析
```mermaid
graph TB
subgraph "TUI特点"
A[文本用户界面
Text User Interface] --> A1[轻量级
Lightweight]
A --> A2[跨平台
Cross Platform]
A --> A3[键盘友好
Keyboard Friendly]
A --> A4[资源占用少
Low Resource]
A --> A5[远程友好
Remote Friendly]
end
subgraph "GUI特点"
B[图形用户界面
Graphical User Interface] --> B1[视觉丰富
Rich Visual]
B --> B2[鼠标交互
Mouse Interaction]
B --> B3[多媒体支持
Multimedia Support]
B --> B4[直观易用
Intuitive]
B --> B5[现代外观
Modern Look]
end
subgraph "Chat-Room TUI优势"
C[开发效率高
Fast Development]
D[部署简单
Easy Deployment]
E[调试方便
Easy Debugging]
F[适合服务器
Server Friendly]
G[专业感强
Professional Feel]
end
A --> C
A --> D
A --> E
A --> F
A --> G
style A fill:#e8f5e8
style B fill:#fff3cd
style C fill:#f8d7da
```
### Chat-Room TUI架构设计
```mermaid
graph LR
subgraph "Chat-Room TUI架构"
A[主应用
Main App] --> B[布局管理器
Layout Manager]
B --> C[聊天窗口
Chat Window]
B --> D[输入框
Input Box]
B --> E[侧边栏
Sidebar]
B --> F[状态栏
Status Bar]
C --> C1[消息列表
Message List]
C --> C2[滚动控制
Scroll Control]
C --> C3[消息渲染
Message Render]
E --> E1[用户列表
User List]
E --> E2[群组列表
Group List]
E --> E3[文件列表
File List]
F --> F1[连接状态
Connection Status]
F --> F2[通知提示
Notifications]
F --> F3[快捷键提示
Hotkey Tips]
end
subgraph "交互组件"
G[键盘处理
Keyboard Handler]
H[主题系统
Theme System]
I[动画效果
Animation]
J[响应式布局
Responsive Layout]
end
A --> G
A --> H
A --> I
A --> J
style A fill:#e8f5e8
style B fill:#fff3cd
style C fill:#f8d7da
```
## 🎨 TUI设计原则
### 现代TUI设计实现
```python
# client/tui/design_principles.py - TUI设计原则
from typing import Dict, List, Tuple, Optional, Any
from dataclasses import dataclass
from enum import Enum
import re
class ColorScheme(Enum):
"""颜色方案"""
DARK = "dark"
LIGHT = "light"
HIGH_CONTRAST = "high_contrast"
CUSTOM = "custom"
class LayoutType(Enum):
"""布局类型"""
HORIZONTAL = "horizontal"
VERTICAL = "vertical"
GRID = "grid"
STACK = "stack"
@dataclass
class DesignToken:
"""设计令牌"""
name: str
value: Any
category: str
description: str
@dataclass
class ColorPalette:
"""颜色调色板"""
primary: str
secondary: str
accent: str
background: str
surface: str
text_primary: str
text_secondary: str
success: str
warning: str
error: str
info: str
class TUIDesignSystem:
"""
TUI设计系统
功能:
1. 统一的设计语言和视觉规范
2. 可复用的组件和样式
3. 响应式布局和自适应设计
4. 无障碍访问和键盘导航
"""
def __init__(self):
# 设计令牌
self.design_tokens: Dict[str, DesignToken] = {}
# 颜色调色板
self.color_palettes: Dict[ColorScheme, ColorPalette] = {}
# 布局规则
self.layout_rules: Dict[str, Dict[str, Any]] = {}
# 组件样式
self.component_styles: Dict[str, Dict[str, Any]] = {}
# 初始化默认设计系统
self._init_design_tokens()
self._init_color_palettes()
self._init_layout_rules()
self._init_component_styles()
def _init_design_tokens(self):
"""初始化设计令牌"""
tokens = [
# 间距
DesignToken("spacing-xs", 1, "spacing", "极小间距"),
DesignToken("spacing-sm", 2, "spacing", "小间距"),
DesignToken("spacing-md", 4, "spacing", "中等间距"),
DesignToken("spacing-lg", 8, "spacing", "大间距"),
DesignToken("spacing-xl", 16, "spacing", "极大间距"),
# 字体大小
DesignToken("font-size-xs", 10, "typography", "极小字体"),
DesignToken("font-size-sm", 12, "typography", "小字体"),
DesignToken("font-size-md", 14, "typography", "中等字体"),
DesignToken("font-size-lg", 16, "typography", "大字体"),
DesignToken("font-size-xl", 20, "typography", "极大字体"),
# 边框
DesignToken("border-width-thin", 1, "border", "细边框"),
DesignToken("border-width-thick", 2, "border", "粗边框"),
DesignToken("border-radius-sm", 2, "border", "小圆角"),
DesignToken("border-radius-md", 4, "border", "中圆角"),
# 阴影
DesignToken("shadow-sm", "▒", "shadow", "小阴影"),
DesignToken("shadow-md", "▓", "shadow", "中阴影"),
DesignToken("shadow-lg", "█", "shadow", "大阴影"),
# 动画
DesignToken("animation-fast", 0.1, "animation", "快速动画"),
DesignToken("animation-normal", 0.2, "animation", "正常动画"),
DesignToken("animation-slow", 0.3, "animation", "慢速动画"),
]
for token in tokens:
self.design_tokens[token.name] = token
def _init_color_palettes(self):
"""初始化颜色调色板"""
# 深色主题
dark_palette = ColorPalette(
primary="#00D4FF", # 青色主色
secondary="#FF6B6B", # 红色辅助色
accent="#4ECDC4", # 青绿色强调色
background="#1A1A1A", # 深灰背景
surface="#2D2D2D", # 表面色
text_primary="#FFFFFF", # 主要文本
text_secondary="#B0B0B0", # 次要文本
success="#4CAF50", # 成功色
warning="#FF9800", # 警告色
error="#F44336", # 错误色
info="#2196F3" # 信息色
)
# 浅色主题
light_palette = ColorPalette(
primary="#0066CC", # 蓝色主色
secondary="#E91E63", # 粉色辅助色
accent="#009688", # 青色强调色
background="#FFFFFF", # 白色背景
surface="#F5F5F5", # 表面色
text_primary="#212121", # 主要文本
text_secondary="#757575", # 次要文本
success="#4CAF50", # 成功色
warning="#FF9800", # 警告色
error="#F44336", # 错误色
info="#2196F3" # 信息色
)
# 高对比度主题
high_contrast_palette = ColorPalette(
primary="#FFFF00", # 黄色主色
secondary="#FF0000", # 红色辅助色
accent="#00FF00", # 绿色强调色
background="#000000", # 黑色背景
surface="#333333", # 表面色
text_primary="#FFFFFF", # 白色文本
text_secondary="#CCCCCC", # 次要文本
success="#00FF00", # 成功色
warning="#FFFF00", # 警告色
error="#FF0000", # 错误色
info="#00FFFF" # 信息色
)
self.color_palettes = {
ColorScheme.DARK: dark_palette,
ColorScheme.LIGHT: light_palette,
ColorScheme.HIGH_CONTRAST: high_contrast_palette
}
def _init_layout_rules(self):
"""初始化布局规则"""
self.layout_rules = {
"chat_window": {
"min_width": 80,
"min_height": 24,
"preferred_ratio": "16:9",
"responsive_breakpoints": {
"small": 80,
"medium": 120,
"large": 160
}
},
"sidebar": {
"min_width": 20,
"max_width": 40,
"default_width": 25,
"collapsible": True
},
"input_area": {
"min_height": 3,
"max_height": 10,
"auto_expand": True
},
"status_bar": {
"height": 1,
"always_visible": True,
"position": "bottom"
}
}
def _init_component_styles(self):
"""初始化组件样式"""
self.component_styles = {
"message_bubble": {
"padding": self.get_token("spacing-sm"),
"margin": self.get_token("spacing-xs"),
"border_style": "rounded",
"max_width": "70%"
},
"user_list_item": {
"padding": self.get_token("spacing-xs"),
"hover_highlight": True,
"status_indicator": True
},
"input_field": {
"padding": self.get_token("spacing-sm"),
"border_style": "solid",
"focus_highlight": True
},
"button": {
"padding": f"{self.get_token('spacing-xs')} {self.get_token('spacing-sm')}",
"border_style": "rounded",
"hover_effect": True,
"press_effect": True
},
"modal": {
"padding": self.get_token("spacing-md"),
"border_style": "double",
"shadow": self.get_token("shadow-md"),
"backdrop": True
}
}
def get_token(self, token_name: str) -> Any:
"""获取设计令牌值"""
token = self.design_tokens.get(token_name)
return token.value if token else None
def get_color_palette(self, scheme: ColorScheme) -> ColorPalette:
"""获取颜色调色板"""
return self.color_palettes.get(scheme, self.color_palettes[ColorScheme.DARK])
def get_layout_rule(self, component: str, rule: str = None) -> Any:
"""获取布局规则"""
component_rules = self.layout_rules.get(component, {})
if rule:
return component_rules.get(rule)
return component_rules
def get_component_style(self, component: str, property: str = None) -> Any:
"""获取组件样式"""
component_style = self.component_styles.get(component, {})
if property:
return component_style.get(property)
return component_style
def calculate_responsive_layout(self, terminal_width: int, terminal_height: int) -> Dict[str, Dict[str, int]]:
"""计算响应式布局"""
layout = {}
# 聊天窗口布局
chat_rules = self.get_layout_rule("chat_window")
sidebar_rules = self.get_layout_rule("sidebar")
# 确定侧边栏宽度
if terminal_width >= chat_rules["responsive_breakpoints"]["large"]:
sidebar_width = sidebar_rules["default_width"]
elif terminal_width >= chat_rules["responsive_breakpoints"]["medium"]:
sidebar_width = sidebar_rules["min_width"]
else:
sidebar_width = 0 # 小屏幕隐藏侧边栏
# 计算各区域尺寸
layout["sidebar"] = {
"width": sidebar_width,
"height": terminal_height - 2, # 减去状态栏
"x": 0,
"y": 0
}
layout["chat_area"] = {
"width": terminal_width - sidebar_width,
"height": terminal_height - 5, # 减去输入区域和状态栏
"x": sidebar_width,
"y": 0
}
layout["input_area"] = {
"width": terminal_width - sidebar_width,
"height": 3,
"x": sidebar_width,
"y": terminal_height - 4
}
layout["status_bar"] = {
"width": terminal_width,
"height": 1,
"x": 0,
"y": terminal_height - 1
}
return layout
class AccessibilityHelper:
"""无障碍访问助手"""
def __init__(self):
self.screen_reader_mode = False
self.high_contrast_mode = False
self.keyboard_navigation_enabled = True
def enable_screen_reader_mode(self):
"""启用屏幕阅读器模式"""
self.screen_reader_mode = True
# 增加文本描述,减少视觉元素
def enable_high_contrast_mode(self):
"""启用高对比度模式"""
self.high_contrast_mode = True
# 使用高对比度颜色方案
def get_aria_label(self, component_type: str, content: str = "") -> str:
"""生成ARIA标签"""
labels = {
"message": f"消息: {content}",
"user_list": "用户列表",
"input_field": "消息输入框",
"send_button": "发送按钮",
"file_button": "文件按钮",
"emoji_button": "表情按钮"
}
return labels.get(component_type, content)
def get_keyboard_shortcuts(self) -> Dict[str, str]:
"""获取键盘快捷键"""
return {
"ctrl+n": "新建聊天",
"ctrl+o": "打开文件",
"ctrl+s": "保存聊天记录",
"ctrl+q": "退出应用",
"tab": "切换焦点",
"shift+tab": "反向切换焦点",
"enter": "发送消息",
"ctrl+enter": "换行",
"esc": "取消/关闭",
"f1": "帮助",
"f11": "全屏切换"
}
class ResponsiveDesign:
"""响应式设计"""
def __init__(self, design_system: TUIDesignSystem):
self.design_system = design_system
self.breakpoints = {
"xs": 60, # 极小屏幕
"sm": 80, # 小屏幕
"md": 120, # 中等屏幕
"lg": 160, # 大屏幕
"xl": 200 # 极大屏幕
}
def get_screen_size_category(self, width: int, height: int) -> str:
"""获取屏幕尺寸类别"""
if width < self.breakpoints["xs"]:
return "xs"
elif width < self.breakpoints["sm"]:
return "sm"
elif width < self.breakpoints["md"]:
return "md"
elif width < self.breakpoints["lg"]:
return "lg"
else:
return "xl"
def adapt_layout_for_screen(self, width: int, height: int) -> Dict[str, Any]:
"""为屏幕尺寸适配布局"""
category = self.get_screen_size_category(width, height)
adaptations = {
"xs": {
"sidebar_visible": False,
"compact_mode": True,
"single_column": True,
"minimal_ui": True
},
"sm": {
"sidebar_visible": False,
"compact_mode": True,
"single_column": True,
"minimal_ui": False
},
"md": {
"sidebar_visible": True,
"sidebar_width": 20,
"compact_mode": False,
"single_column": False
},
"lg": {
"sidebar_visible": True,
"sidebar_width": 25,
"compact_mode": False,
"single_column": False
},
"xl": {
"sidebar_visible": True,
"sidebar_width": 30,
"compact_mode": False,
"single_column": False,
"extra_panels": True
}
}
return adaptations.get(category, adaptations["md"])
# 使用示例
def demo_tui_design():
"""TUI设计系统演示"""
design_system = TUIDesignSystem()
accessibility = AccessibilityHelper()
responsive = ResponsiveDesign(design_system)
print("=== TUI设计系统演示 ===")
# 获取设计令牌
spacing_md = design_system.get_token("spacing-md")
print(f"中等间距: {spacing_md}")
# 获取颜色调色板
dark_palette = design_system.get_color_palette(ColorScheme.DARK)
print(f"深色主题主色: {dark_palette.primary}")
# 获取组件样式
button_style = design_system.get_component_style("button")
print(f"按钮样式: {button_style}")
# 计算响应式布局
layout = design_system.calculate_responsive_layout(120, 40)
print(f"120x40终端布局: {layout}")
# 无障碍功能
shortcuts = accessibility.get_keyboard_shortcuts()
print(f"键盘快捷键: {list(shortcuts.keys())[:5]}...")
# 响应式适配
adaptations = responsive.adapt_layout_for_screen(80, 24)
print(f"80x24屏幕适配: {adaptations}")
if __name__ == "__main__":
demo_tui_design()
```
## 🎯 实践练习
### 练习1:自定义主题系统
```python
class CustomThemeBuilder:
"""
自定义主题构建器练习
要求:
1. 支持用户自定义颜色方案
2. 实现主题的导入导出功能
3. 提供主题预览和实时切换
4. 支持主题的继承和扩展
"""
def create_theme_from_base(self, base_theme: str, modifications: Dict[str, str]):
"""基于基础主题创建新主题"""
# TODO: 实现主题创建逻辑
pass
def export_theme_to_file(self, theme_name: str, file_path: str):
"""导出主题到文件"""
# TODO: 实现主题导出
pass
```
### 练习2:动态布局系统
```python
class DynamicLayoutManager:
"""
动态布局管理器练习
要求:
1. 支持拖拽调整面板大小
2. 实现面板的显示/隐藏切换
3. 保存和恢复用户布局偏好
4. 支持多种预设布局模式
"""
def save_layout_preference(self, user_id: int, layout_config: Dict[str, Any]):
"""保存布局偏好"""
# TODO: 实现布局偏好保存
pass
def apply_layout_preset(self, preset_name: str):
"""应用预设布局"""
# TODO: 实现预设布局应用
pass
```
## ✅ 学习检查
完成本章学习后,请确认您能够:
- [ ] 理解TUI设计的基本概念和优势
- [ ] 掌握现代TUI设计原则和最佳实践
- [ ] 设计统一的视觉规范和组件系统
- [ ] 实现响应式布局和无障碍访问
- [ ] 创建可复用的设计令牌和样式
- [ ] 完成实践练习
## 📚 下一步
TUI概念与设计掌握后,请继续学习:
- [Textual框架](textual-framework.md)
- [组件设计](component-design.md)
- [主题系统](theme-system.md)
---
**现在您已经掌握了现代TUI设计的核心理念!** 🎨