save code
This commit is contained in:
+183
-124
@@ -1,143 +1,143 @@
|
||||
# 第 1 课:GUI 你好窗口
|
||||
|
||||
**本课目标**:用 FLTK 创建第一个 GUI 程序——一个窗口,里面写着欢迎语。理解 GUI 程序和命令行程序的不同。
|
||||
|
||||
**复习**:你会用 `std::cout` 在命令行输出文字。本课把"输出"搬到**窗口**里。
|
||||
> **本课目标**:从一个空空的程序开始,**自己一步一步**让屏幕上弹出一个写着 "欢迎来到抽卡世界!" 的窗口。
|
||||
>
|
||||
> **预计时间**:30~40 分钟
|
||||
>
|
||||
> **学完之后**:你能解释什么是窗口、控件、事件循环,并能凭记忆写出 5~6 行让窗口出现的代码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 这一课要搞懂什么
|
||||
## 0. 上课前先理理
|
||||
|
||||
### 命令行 vs GUI
|
||||
### 命令行程序 vs GUI 程序
|
||||
|
||||
| | 命令行程序 | GUI 程序 |
|
||||
|---|---|---|
|
||||
| 样子 | 黑底白字 | 有窗口、按钮 |
|
||||
| 运行方式 | 一口气跑完 | 一直等用户操作 |
|
||||
| 输出 | `cout` 打印到终端 | 显示在窗口里的控件上 |
|
||||
| 用户操作 | 键盘输入 | 鼠标点击、键盘 |
|
||||
| | 命令行(以前的你) | GUI(现在的你) |
|
||||
|---------|-----------------------------|----------------------------------|
|
||||
| 长什么样 | 黑底白字一行行 | 有窗口、按钮、彩色文字 |
|
||||
| 怎么跑 | 一口气从上跑到下,跑完就退出 | 启动后 **一直在等** 你点鼠标 |
|
||||
| 怎么输出 | `std::cout << ...` | 把文字 "贴" 在窗口上的便利贴上 |
|
||||
|
||||
### 本课三个"新朋友"
|
||||
### 这一课会认识 3 个新朋友
|
||||
|
||||
| FLTK 类 | 比喻(小朋友版) | 作用 |
|
||||
|---------|-----------------|------|
|
||||
| `Fl_Window` | 画纸 | 程序的窗口框架 |
|
||||
| `Fl_Box` | 便利贴 | 在窗口上显示文字 |
|
||||
| `Fl::run()` | 不休息的保安 | 让窗口一直显示,等用户操作 |
|
||||
| 名字 | 比喻 | 干什么用 |
|
||||
|---------------|------------------|-------------------------------------|
|
||||
| `Fl_Window` | **画纸** | 程序的窗口本体 |
|
||||
| `Fl_Box` | **便利贴** | 贴在画纸上的一段文字 |
|
||||
| `Fl::run()` | **不下班的保安** | 让窗口一直待着,等你操作 |
|
||||
|
||||
### 你还认识一个老朋友的变身
|
||||
|
||||
- `new`:以前你可能没见过。在 FLTK 里,控件用 `new` 创建。可以简单理解为"变出一个控件来"。
|
||||
> 💡 还会顺带认识两个 C++ 新写法:`new` 和 `->`。先不用搞懂原理,跟着写就行。
|
||||
|
||||
---
|
||||
|
||||
## 2. 第一个 GUI 程序
|
||||
## 1. 起步:打开起步骨架,先编译一次
|
||||
|
||||
保存为 `lesson_gui_1.cpp`:
|
||||
打开 `lesson_gui_1.cpp`。你会看到一个全是 TODO 的骨架。
|
||||
|
||||
```cpp
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
先在终端里编译一下,确认环境没问题:
|
||||
|
||||
int main() {
|
||||
// 创建窗口:宽 400,高 300,标题"抽卡世界"
|
||||
Fl_Window *window = new Fl_Window(400, 300, "抽卡世界");
|
||||
|
||||
// 在窗口上放一个文字标签:位置(50,100),宽300,高100
|
||||
Fl_Box *box = new Fl_Box(50, 100, 300, 100, "欢迎来到抽卡世界!");
|
||||
box->labelsize(24); // 字号 24
|
||||
|
||||
// 结束添加控件
|
||||
window->end();
|
||||
// 显示窗口
|
||||
window->show();
|
||||
// 进入事件循环(让窗口一直显示)
|
||||
return Fl::run();
|
||||
}
|
||||
**Mac / Linux:**
|
||||
```bash
|
||||
cmake -B build -DLESSON=1
|
||||
cmake --build build
|
||||
./build/gacha1
|
||||
```
|
||||
|
||||
配套 `CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(gacha1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options("/utf-8")
|
||||
endif()
|
||||
|
||||
find_package(FLTK CONFIG REQUIRED)
|
||||
|
||||
add_executable(gacha1 lesson_gui_1.cpp)
|
||||
target_link_libraries(gacha1 PRIVATE fltk)
|
||||
**Windows:**
|
||||
```bash
|
||||
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`,运行前两行就够**:
|
||||
> ```bash
|
||||
> cmake --build build && ./build/gacha1
|
||||
> ```
|
||||
|
||||
### ✅ 检查点 1
|
||||
- [ ] 编译没报错
|
||||
- [ ] 运行后……什么都没发生?**对的**!程序进了 `main`,立刻就 `return 0` 退出了。我们接下来一步步让它"活"起来。
|
||||
|
||||
---
|
||||
|
||||
## 3. 代码逐行解释
|
||||
## 2. 步骤 1:变出一个空窗口
|
||||
|
||||
```cpp
|
||||
#include <FL/Fl.H> // FLTK 核心
|
||||
#include <FL/Fl_Window.H> // 窗口
|
||||
#include <FL/Fl_Box.H> // 文字标签
|
||||
```
|
||||
### 🤔 先想一下
|
||||
- 一个窗口至少要告诉电脑哪些信息?(提示:长什么样、叫什么)
|
||||
- 程序跑完 `main` 就结束了——那窗口怎么能"一直待着"呢?
|
||||
|
||||
以前的 `#include <iostream>` 是请命令行工具,这三个 `#include <FL/...>` 是请窗口工具。
|
||||
### ✏️ 在 `main` 里加这 3 件事
|
||||
|
||||
**(1) 创建窗口对象**:替换掉 `// TODO(步骤 1):在这里创建一个窗口对象 window` 那一行:
|
||||
|
||||
```cpp
|
||||
Fl_Window *window = new Fl_Window(400, 300, "抽卡世界");
|
||||
```
|
||||
|
||||
- `Fl_Window` = 窗口
|
||||
- `400, 300` = 宽 400 像素,高 300 像素
|
||||
- `"抽卡世界"` = 窗口标题栏上显示的字
|
||||
- `new` = 创建一个窗口(和以前 `int x = 5` 不同,窗口是"对象")
|
||||
- `*` = 指针(先记:FLTK 创建的控件都用 `*`)
|
||||
读法:用 `new` "变出" 一个 `Fl_Window`,宽 400、高 300,标题叫 "抽卡世界"。`window` 是它的名字。
|
||||
|
||||
**(2) 显示窗口**:替换掉 `// TODO(步骤 1):告诉窗口"控件加完了"…` 那一行:
|
||||
|
||||
```cpp
|
||||
window->end();
|
||||
window->show();
|
||||
```
|
||||
|
||||
读法:`->` 念 "的"。`window` 的 `end()` 表示"控件加完啦",`window` 的 `show()` 是"露面"。
|
||||
|
||||
**(3) 让程序"待住"不退出**:把最后的 `return 0;` 改成:
|
||||
|
||||
```cpp
|
||||
return Fl::run();
|
||||
```
|
||||
|
||||
`Fl::run()` 就是那个不下班的保安——它会卡在这里,直到你关闭窗口才返回。
|
||||
|
||||
### ▶️ 编译运行
|
||||
|
||||
```bash
|
||||
cmake --build build && ./build/gacha1
|
||||
```
|
||||
|
||||
### ✅ 检查点 2
|
||||
- [ ] 屏幕上出现一个 400×300 的空白窗口
|
||||
- [ ] 窗口标题栏写着 "抽卡世界"
|
||||
- [ ] 关闭窗口,程序退出,终端回到提示符
|
||||
|
||||
🎉 你的第一个 GUI 程序!但里面空空的,下一步贴张便利贴上去。
|
||||
|
||||
> ❓ 如果没出来:是不是忘了把 `return 0;` 改成 `return Fl::run();`?
|
||||
|
||||
---
|
||||
|
||||
## 3. 步骤 2:在窗口里贴一张便利贴
|
||||
|
||||
### 🤔 先想一下
|
||||
- 贴在窗口里的文字,应该放在窗口的哪个位置?怎么告诉电脑这个位置?
|
||||
- 文字框本身也是一个"东西",那它要不要 `new` 出来?
|
||||
|
||||
### ✏️ 改两个地方
|
||||
|
||||
**(1) 顶上的 include 区**:把 `// TODO(步骤 2)`:还需要再 `#include 一个头文件` 这行替换成:
|
||||
|
||||
```cpp
|
||||
#include <FL/Fl_Box.H>
|
||||
```
|
||||
|
||||
便利贴在 FLTK 里叫 `Fl_Box`,要先告诉编译器去哪儿找。
|
||||
|
||||
**(2) 在 `window->end();` **之前**插入这一行**(替换掉对应的 TODO):
|
||||
|
||||
```cpp
|
||||
Fl_Box *box = new Fl_Box(50, 100, 300, 100, "欢迎来到抽卡世界!");
|
||||
```
|
||||
|
||||
- `50, 100` = 文字在窗口中的位置:从左边数 50 像素,从上边数 100 像素
|
||||
- `300, 100` = 文字区域的宽 300,高 100
|
||||
- 最后一个参数 = 显示的文字内容
|
||||
读法:变出一个 `Fl_Box`,位置 (50, 100),宽 300、高 100,文字是 "欢迎来到抽卡世界!"。
|
||||
|
||||
```cpp
|
||||
box->labelsize(24);
|
||||
```
|
||||
### 📐 坐标到底怎么数?
|
||||
|
||||
`->` 读作"的":`box` 的 `labelsize` 设为 24(字号)。
|
||||
|
||||
```cpp
|
||||
window->end(); // 告诉窗口:控件加完了
|
||||
window->show(); // 让窗口显示出来
|
||||
return Fl::run(); // 进入"等待模式",窗口一直显示直到用户关闭
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 编译和运行
|
||||
|
||||
**Mac**:
|
||||
```bash
|
||||
cmake -B build && cmake --build build && ./build/gacha1
|
||||
```
|
||||
|
||||
**Windows**(VS + vcpkg):
|
||||
```bash
|
||||
cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake && cmake --build build --config Release && build\Release\gacha1.exe
|
||||
```
|
||||
|
||||
运行后应看到一个白色窗口,中间写着"欢迎来到抽卡世界!"。
|
||||
|
||||
---
|
||||
|
||||
## 5. 坐标系统
|
||||
|
||||
FLTK 窗口的坐标系:
|
||||
FLTK 的坐标系——和你以前数学课上的不一样,Y 是 **向下** 增大的:
|
||||
|
||||
```
|
||||
(0,0) ──────────── X →
|
||||
@@ -145,7 +145,7 @@ FLTK 窗口的坐标系:
|
||||
│ (50,100)
|
||||
│ ↓
|
||||
│ ┌──────────┐
|
||||
│ │ 文字区域 │ 宽 300
|
||||
│ │ 便利贴 │ 宽 300
|
||||
│ │ │ 高 100
|
||||
│ └──────────┘
|
||||
Y
|
||||
@@ -154,41 +154,100 @@ FLTK 窗口的坐标系:
|
||||
|
||||
- X 从左到右增大
|
||||
- Y 从上到下增大
|
||||
- 原点 (0,0) 在左上角
|
||||
- 原点 (0,0) 在窗口左上角
|
||||
|
||||
### ▶️ 编译运行
|
||||
|
||||
```bash
|
||||
cmake --build build && ./build/gacha1
|
||||
```
|
||||
|
||||
### ✅ 检查点 3
|
||||
- [ ] 窗口里出现了 "欢迎来到抽卡世界!"
|
||||
- [ ] 文字大概在窗口中间偏上的位置
|
||||
|
||||
> ❓ 文字看起来很小?没事,下一步搞定字号。
|
||||
|
||||
---
|
||||
|
||||
## 6. 本课课堂小练习
|
||||
## 4. 步骤 3:让字大起来
|
||||
|
||||
1. 把窗口标题 `"抽卡世界"` 改成你自己起的名字。
|
||||
2. 把欢迎语改成你喜欢的句子。
|
||||
3. 试试把窗口改大(比如 600×400),把 `Box` 的位置也调整一下,让文字居中。
|
||||
### ✏️ 在 `new Fl_Box(...)` 那行的**下一行**加:
|
||||
|
||||
```cpp
|
||||
box->labelsize(24);
|
||||
```
|
||||
|
||||
意思是:`box` 的 `labelsize`(标签字号)设为 24。
|
||||
|
||||
### ▶️ 编译运行
|
||||
|
||||
```bash
|
||||
cmake --build build && ./build/gacha1
|
||||
```
|
||||
|
||||
### ✅ 检查点 4
|
||||
- [ ] 文字明显变大了
|
||||
- [ ] 整个窗口看起来终于像个迎宾画面了
|
||||
|
||||
🎉 **第 1 课通关!** 你刚刚自己写出了一个完整 GUI 程序。
|
||||
|
||||
---
|
||||
|
||||
## 7. 本课课后作业(选做)
|
||||
## 5. 玩一玩:改一改看会发生什么
|
||||
|
||||
- 把 `labelsize(24)` 改成 36、12、8,看文字大小变化。试试极端值,看会发生什么。
|
||||
- 在窗口上放**两个** `Fl_Box`,一个在上半部分,一个在下半部分,显示不同的文字。
|
||||
- 思考题:和以前 `std::cout` 输出文字相比,GUI 方式有什么不同?(一个提示:GUI 的文字可以"一直显示",不用 `\n` 刷屏。)
|
||||
每改一个,记得 **保存 → 编译 → 运行**:
|
||||
|
||||
1. 把 `400, 300` 改成 `600, 400`。窗口变大了吗?
|
||||
2. 把 `labelsize(24)` 改成 `36`、`12`、`80`。极端值会怎样?
|
||||
3. 把 `(50, 100, 300, 100)` 里的 `50` 改成 `0`。文字跑哪儿去了?
|
||||
4. 把窗口标题 `"抽卡世界"` 改成你自己起的名字。
|
||||
5. **思考题**:怎么让文字 **正好居中**?窗口宽 400,文字框宽 300——X 应该填几?
|
||||
|
||||
> 💡 想到答案再往下看:宽 400 减宽 300 等于 100,两边各留 50,所以 X = 50。本课的代码刚好就是居中的。
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见小问题
|
||||
## 6. 课后挑战(选做)
|
||||
|
||||
- **窗口一闪就消失**:检查有没有 `return Fl::run();`,不是 `return 0;`。
|
||||
- **编译报错找不到 FL**:检查 `CMakeLists.txt` 里的 `find_package(FLTK CONFIG REQUIRED)` 是否正确,以及 FLTK 是否已安装。
|
||||
- **中文乱码**:确保源文件保存为 UTF-8 编码。Mac clang++ 一般默认支持。
|
||||
- **窗口出来了但是空的**:检查 `window->show()` 和 `window->end()` 的顺序。`show()` 必须在 `end()` 之后(本课代码顺序已正确)。
|
||||
完成以上后还想多玩?挑一个:
|
||||
|
||||
- **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. 偷看老师答案
|
||||
|
||||
写不出来、或者想对一下?
|
||||
|
||||
```bash
|
||||
cmake -B build -DLESSON=1 -DUSE_SOLUTION=ON
|
||||
cmake --build build && ./build/gacha1
|
||||
```
|
||||
|
||||
源文件路径:`solutions/lesson_gui_1.cpp`。
|
||||
**但请尽量自己先动手——卡住 5 分钟以上再看**。
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一课预告
|
||||
|
||||
第 2 课学**按钮和回调**:在窗口上放一个"抽卡"按钮,点一下,文字会变——从"只看不动"到"能交互"。
|
||||
第 2 课:加一个 **按钮**,点一下让窗口里的字变样子。你将第一次见到"用户操作 → 程序响应"的 GUI 核心套路:**回调函数**。
|
||||
|
||||
---
|
||||
> 📝 学完记得在 `学习进度与状态-GUI.md` 里把第 1 课打勾。
|
||||
|
||||
*学完请在 `学习进度与状态-GUI.md` 里更新第 1 课的状态。*
|
||||
|
||||
**下一课文档**:`第02课-按钮与回调.md`
|
||||
**下一课**:`第02课-按钮与回调.md`
|
||||
|
||||
Reference in New Issue
Block a user