7.9 KiB
第 1 课:GUI 你好窗口
本课目标:从一个空空的程序开始,自己一步一步让屏幕上弹出一个写着 "欢迎来到抽卡世界!" 的窗口。
预计时间:30~40 分钟
学完之后:你能解释什么是窗口、控件、事件循环,并能凭记忆写出 5~6 行让窗口出现的代码。
0. 上课前先理理
命令行程序 vs GUI 程序
| 命令行(以前的你) | GUI(现在的你) | |
|---|---|---|
| 长什么样 | 黑底白字一行行 | 有窗口、按钮、彩色文字 |
| 怎么跑 | 一口气从上跑到下,跑完就退出 | 启动后 一直在等 你点鼠标 |
| 怎么输出 | std::cout << ... |
把文字 "贴" 在窗口上的便利贴上 |
这一课会认识 3 个新朋友
| 名字 | 比喻 | 干什么用 |
|---|---|---|
Fl_Window |
画纸 | 程序的窗口本体 |
Fl_Box |
便利贴 | 贴在画纸上的一段文字 |
Fl::run() |
不下班的保安 | 让窗口一直待着,等你操作 |
💡 还会顺带认识两个 C++ 新写法:
new和->。先不用搞懂原理,跟着写就行。
1. 起步:打开起步骨架,先编译一次
打开 lesson_gui_1.cpp。你会看到一个全是 TODO 的骨架。
先在终端里编译一下,确认环境没问题:
Mac / Linux:
cmake -B build -DLESSON=1
cmake --build build
./build/gacha1
Windows:
cmake -B build -DLESSON=1 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build --config Release
build\Release\gacha1.exe
💡 第一次
cmake -B build -DLESSON=1之后,后面每次只要改了.cpp,运行前两行就够:cmake --build build && ./build/gacha1
✅ 检查点 1
- 编译没报错
- 运行后……什么都没发生?对的!程序进了
main,立刻就return 0退出了。我们接下来一步步让它"活"起来。
2. 步骤 1:变出一个空窗口
🤔 先想一下
- 一个窗口至少要告诉电脑哪些信息?(提示:长什么样、叫什么)
- 程序跑完
main就结束了——那窗口怎么能"一直待着"呢?
✏️ 在 main 里加这 3 件事
(1) 创建窗口对象:替换掉 // TODO(步骤 1):在这里创建一个窗口对象 window 那一行:
Fl_Window *window = new Fl_Window(400, 300, "抽卡世界");
读法:用 new "变出" 一个 Fl_Window,宽 400、高 300,标题叫 "抽卡世界"。window 是它的名字。
(2) 显示窗口:替换掉 // TODO(步骤 1):告诉窗口"控件加完了"… 那一行:
window->end();
window->show();
读法:-> 念 "的"。window 的 end() 表示"控件加完啦",window 的 show() 是"露面"。
(3) 让程序"待住"不退出:把最后的 return 0; 改成:
return Fl::run();
Fl::run() 就是那个不下班的保安——它会卡在这里,直到你关闭窗口才返回。
▶️ 编译运行
cmake --build build && ./build/gacha1
✅ 检查点 2
- 屏幕上出现一个 400×300 的空白窗口
- 窗口标题栏写着 "抽卡世界"
- 关闭窗口,程序退出,终端回到提示符
🎉 你的第一个 GUI 程序!但里面空空的,下一步贴张便利贴上去。
❓ 如果没出来:是不是忘了把
return 0;改成return Fl::run();?
3. 步骤 2:在窗口里贴一张便利贴
🤔 先想一下
- 贴在窗口里的文字,应该放在窗口的哪个位置?怎么告诉电脑这个位置?
- 文字框本身也是一个"东西",那它要不要
new出来?
✏️ 改两个地方
(1) 顶上的 include 区:把 // TODO(步骤 2):还需要再 #include 一个头文件 这行替换成:
#include <FL/Fl_Box.H>
便利贴在 FLTK 里叫 Fl_Box,要先告诉编译器去哪儿找。
(2) 在 window->end(); 之前插入这一行(替换掉对应的 TODO):
Fl_Box *box = new Fl_Box(50, 100, 300, 100, "欢迎来到抽卡世界!");
读法:变出一个 Fl_Box,位置 (50, 100),宽 300、高 100,文字是 "欢迎来到抽卡世界!"。
📐 坐标到底怎么数?
FLTK 的坐标系——和你以前数学课上的不一样,Y 是 向下 增大的:
(0,0) ──────────── X →
│
│ (50,100)
│ ↓
│ ┌──────────┐
│ │ 便利贴 │ 宽 300
│ │ │ 高 100
│ └──────────┘
Y
↓
- X 从左到右增大
- Y 从上到下增大
- 原点 (0,0) 在窗口左上角
▶️ 编译运行
cmake --build build && ./build/gacha1
✅ 检查点 3
- 窗口里出现了 "欢迎来到抽卡世界!"
- 文字大概在窗口中间偏上的位置
❓ 文字看起来很小?没事,下一步搞定字号。
4. 步骤 3:让字大起来
✏️ 在 new Fl_Box(...) 那行的下一行加:
box->labelsize(24);
意思是:box 的 labelsize(标签字号)设为 24。
▶️ 编译运行
cmake --build build && ./build/gacha1
✅ 检查点 4
- 文字明显变大了
- 整个窗口看起来终于像个迎宾画面了
🎉 第 1 课通关! 你刚刚自己写出了一个完整 GUI 程序。
5. 玩一玩:改一改看会发生什么
每改一个,记得 保存 → 编译 → 运行:
- 把
400, 300改成600, 400。窗口变大了吗? - 把
labelsize(24)改成36、12、80。极端值会怎样? - 把
(50, 100, 300, 100)里的50改成0。文字跑哪儿去了? - 把窗口标题
"抽卡世界"改成你自己起的名字。 - 思考题:怎么让文字 正好居中?窗口宽 400,文字框宽 300——X 应该填几?
💡 想到答案再往下看:宽 400 减宽 300 等于 100,两边各留 50,所以 X = 50。本课的代码刚好就是居中的。
6. 课后挑战(选做)
完成以上后还想多玩?挑一个:
- A. 双便利贴:在窗口的上、下半部分各放一个
Fl_Box,写不同的话。 - B. 给画纸刷色:在
window->end()前面加window->color(fl_rgb_color(255, 240, 200));,看看效果。(需要#include <FL/fl_draw.H>或加在已有头文件里编译试一下,错了就回滚——这就是学编程) - C. 思考题:以前
std::cout一打印就显示,再换一行又能继续打印。GUI 的Fl_Box行为有什么不一样?
7. 卡住了?常见小问题
| 现象 | 多半是 |
|---|---|
| 窗口闪一下就消失 | return 0; 没改成 return Fl::run(); |
编译报 "找不到 Fl_Box" |
忘了 #include <FL/Fl_Box.H> |
编译报 "找不到 FL/Fl.H" |
FLTK 没装好;Mac 跑一下 brew install fltk cmake |
| 窗口出来但是全黑/全空白 | window->show() 是不是写在 window->end() 前面了?顺序反了 |
| 中文变方框 | 文件没存成 UTF-8 编码 |
8. 偷看老师答案
写不出来、或者想对一下?
cmake -B build -DLESSON=1 -DUSE_SOLUTION=ON
cmake --build build && ./build/gacha1
源文件路径:solutions/lesson_gui_1.cpp。
但请尽量自己先动手——卡住 5 分钟以上再看。
9. 下一课预告
第 2 课:加一个 按钮,点一下让窗口里的字变样子。你将第一次见到"用户操作 → 程序响应"的 GUI 核心套路:回调函数。
📝 学完记得在
学习进度与状态-GUI.md里把第 1 课打勾。
下一课:第02课-按钮与回调.md