GUI——用户流程从屏幕网格到自由窗口
TUI在终端的字符网格上划分区域,焦点在区域之间转移。GUI打破了网格的限制——窗口可以任意大小、任意位置、任意层叠;控件可以是按钮、滑块、下拉菜单;用户用鼠标点击而不是键盘导航。用户流程从「在网格中移动焦点」变成了「在窗口中操作控件」。
一、从网格到自由布局 TUI的屏幕是字符网格——每个位置只能放一个字符,区域只能是矩形。
GUI的屏幕是像素——任意位置可以放任意形状的控件,窗口可以层叠、缩放、拖拽。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 TUI: ┌────────────────────────┐ │ Region A │ Region B │ │ │ │ ├──────────┴─────────────┤ │ Region C │ └────────────────────────┘ GUI: ┌──────────────┐ │ Window A │ ← 可以拖拽、缩放 │ ┌────────┐ │ │ │ Button │ │ ← 鼠标点击 │ └────────┘ │ │ ┌──────────────┐ │ │ Slider │← 可以拖拽 │ └──────────────┘ └──────────────┘ ┌──────────────┐ │ Window B │ ← 层叠在 A 上面 └──────────────┘
GUI的核心变化:用户交互从键盘扩展到了鼠标。 键盘是一维的(一次一个字符),鼠标是二维的(可以在屏幕上任意位置点击)。
二、控件:GUI的交互单元 TUI的交互单元是「区域」——区域是一块屏幕空间。GUI的交互单元是「控件」——控件是有行为的屏幕元素。
1 2 3 4 5 6 按钮(Button):点击 → 触发动作 文本框(TextField):点击 → 获得焦点 → 输入文字 滑块(Slider):拖拽 → 调整数值 复选框(Checkbox):点击 → 切换选中状态 下拉菜单(ComboBox):点击 → 展开选项列表 → 选择 列表(ListView):点击 → 选中项目 → 双击 → 打开
每个控件有自己的:
1 2 3 4 1. 外观:画在屏幕上的样子 2. 状态:是否聚焦、是否禁用、当前值 3. 行为:接收输入后做什么 4. 事件:状态变化时通知谁
控件 = 外观 + 状态 + 行为 + 事件。 这就是面向对象中「封装」的直接体现。
三、控件树:GUI的层级结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 graph TD W["Window"] --> MB["MenuBar"] W --> TB["Toolbar"] W --> CP["ContentPanel"] W --> SB["StatusBar"] MB --> MI1["MenuItem: File"] MB --> MI2["MenuItem: Edit"] TB --> BTN1["Button: Open"] TB --> BTN2["Button: Save"] CP --> LV["ListView"] CP --> DP["DetailPanel"] LV --> LI1["ListItem: Item 1"] LV --> LI2["ListItem: Item 2"] DP --> TF["TextField"] DP --> BTN3["Button: OK"] SB --> LB["Label: Ready"] style W fill:#4a9eff,color:#fff style CP fill:#ff9f4a,color:#fff style DP fill:#4caf50,color:#fff
控件不是平铺的——它们形成了树状层级 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Window ├── MenuBar │ ├── MenuItem("File") │ ├── MenuItem("Edit") │ └── MenuItem("Help") ├── Toolbar │ ├── Button("Open") │ ├── Button("Save") │ └── Separator ├── ContentPanel │ ├── ListView │ │ ├── ListItem("Item 1") │ │ ├── ListItem("Item 2") │ │ └── ListItem("Item 3") │ └── DetailPanel │ ├── Label("Name:") │ ├── TextField │ └── Button("OK") └── StatusBar └── Label("Ready")
控件树 = 窗口的骨架。 窗口打开时,控件树被创建;窗口关闭时,控件树被销毁。
控件树的关键属性:
1 2 3 父子关系:子控件属于父控件,父控件关闭时子控件一起关闭 布局关系:子控件的位置和大小由父控件决定 事件传播:子控件的事件可以向上传递给父控件
四、事件循环:GUI的心脏 GUI的程序结构是事件循环 ——和TUI一样,但更复杂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ┌──────────────────────────────────────┐ │ 创建窗口和控件树 │ │ │ │ ┌──────────────────────────────────┐ │ │ │ 等待事件 │ │ ← 事件循环起点 │ │ 鼠标点击? 键盘输入? 窗口调整? │ │ │ │ 定时器? 网络数据? │ │ │ │ ↓ │ │ │ │ 找到事件对应的控件 │ │ │ │ ↓ │ │ │ │ 调用控件的事件处理函数 │ │ │ │ ↓ │ │ │ │ 控件状态变化 │ │ │ │ ↓ │ │ │ │ 重绘需要更新的区域 │ │ │ │ ↓ │ │ │ │ 是退出? ──是──→ 销毁窗口 │ │ │ │ │ 否 │ │ │ │ └──→ 回到等待事件 │ │ ← 事件循环终点 │ └──────────────────────────────────┘ │ └──────────────────────────────────────┘
事件循环处理的事件类型:
1 2 3 4 用户事件:鼠标点击、键盘输入、拖拽 系统事件:窗口调整大小、窗口获得/失去焦点、窗口关闭 控件事件:按钮被点击、文本框内容变化、列表选中项变化 自定义事件:网络数据到达、定时器触发、后台任务完成
事件循环 = GUI的心脏。 它是GUI所有行为的驱动力。
五、事件传播:用户操作由谁响应 GUI 的控制流 是「事件传播」:用户的点击不直接到达控件,而是沿控件树从根向下查找目标,再由目标向上回溯。Window → ContentPanel → Button(OK),找到第一个包含点击位置的控件,控件处理后事件停止传播。事件传播决定了用户的一个操作最终由谁响应 (控制流的跨形态完整演化见 06 第二节)。
六、用户流程的完整路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 flowchart TD A["双击程序图标"] --> B["创建窗口和控件"] B --> C["等待事件"] C --> D{"事件类型?"} D -->|"鼠标点击"| E["找到目标控件"] D -->|"键盘输入"| F["当前焦点控件接收"] D -->|"系统事件"| G["窗口/定时器处理"] E --> H["调用控件事件处理函数"] F --> H G --> H H --> I["控件状态变化"] I --> J["重绘屏幕"] J --> K{"关闭窗口?"} K -->|"否"| C K -->|"是"| L["销毁窗口, 退出"] style C fill:#4a9eff,color:#fff style L fill:#f44,color:#fff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 用户流程(GUI 完整路径): 1. 双击程序图标 2. 等待窗口出现 3. 看到窗口和控件 4. 操作控件: a. 点击按钮 → 看到结果 b. 输入文字 → 看到文字出现 c. 拖拽滑块 → 看到数值变化 d. 选择菜单 → 看到新窗口/对话框 5. 看到窗口更新 6. 重复步骤 4-5 7. 点击关闭按钮 8. 窗口消失,程序退出
和TUI的区别:
1 2 3 4 5 6 7 TUI: 按键 → 焦点转移 → 屏幕重绘 (键盘驱动,焦点在区域间转移) GUI: 点击 → 控件响应 → 窗口更新 (鼠标+键盘驱动,焦点在控件间转移)
GUI的用户流程核心:用户的操作直接作用于控件,控件的响应直接反映在屏幕上。 没有「命令」的概念,也没有「焦点区域」的概念——用户直接操作对象。
七、窗口:GUI的独立交互单元 TUI只有一个屏幕——所有信息都在同一个屏幕上。GUI可以有多个窗口 ——每个窗口是独立的交互单元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 主窗口: ┌──────────────────────┐ │ 菜单栏 │ │ 文件列表 │ │ 状态栏 │ └──────────────────────┘ 弹出对话框: ┌──────────────┐ │ 确认删除? │ │ [是] [否] │ └──────────────┘ 对话框阻塞了主窗口的交互——用户必须先处理对话框
多窗口带来了新的状态:
1 2 3 4 1. 窗口层级:哪个窗口在最前面? 2. 窗口模态:对话框是否阻塞其他窗口? 3. 窗口焦点:用户当前在操作哪个窗口? 4. 窗口间通信:对话框的结果怎么传回主窗口?
多窗口 = 多个独立的事件循环(或一个事件循环处理多个窗口的事件)。
八、GUI vs TUI的本质区别
维度
TUI
GUI
屏幕载体
字符网格
像素窗口
交互单元
区域
控件
输入设备
键盘
鼠标 + 键盘
布局方式
固定网格
自由布局
窗口管理
无(单屏幕)
多窗口、层叠、模态
事件粒度
按键
鼠标点击/拖拽/滚轮 + 按键
阻塞特性
阻塞(执行期间不能操作)
非阻塞(后台执行)
反馈方式
屏幕重绘
控件状态变化
GUI的核心变化:用户交互从「键盘导航」扩展到了「鼠标直接操作」。 鼠标让用户的操作从「告诉程序做什么」(输入命令)变成了「直接对对象做什么」(点击按钮、拖拽滑块)。
九、GUI的用户流程总结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 GUI的用户流程: 启动 → 创建窗口 → [操作控件 → 事件循环 → 窗口更新] × N → 关闭窗口 → 退出 特征: ┌─────────────────────────────────────────────┐ │ 控件树 层级化的交互单元 │ │ 事件循环 鼠标/键盘/系统事件驱动 │ │ 事件传播 沿控件树传递 │ │ 多窗口 独立交互单元,可层叠/模态 │ │ 异步响应 非阻塞,后台执行 │ │ 直接操作 用户直接操作对象 │ │ 丰富反馈 视觉状态变化 │ └─────────────────────────────────────────────┘
GUI相比TUI的核心变化:
维度
TUI
GUI
交互模型
事件循环(键盘)
事件循环(鼠标+键盘)
信息展示
字符网格
像素窗口
用户负担
记住焦点位置
直接看到控件状态
反馈形式
文字重绘
视觉状态变化
窗口管理
无
多窗口、模态、层叠
GUI是单机交互的最高形态。 但当GUI程序需要和其他程序协作——比如一个GUI编辑器需要调用编译器、一个游戏需要加载资源包——单个GUI窗口就不够了。下一篇看多系统GUI——进程间通信与多窗口状态。