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——进程间通信与多窗口状态。