30、Qt综合项目:计算器或记事本实战,综合运用所学知识
终于走到这一步了。说实话,每次我带新人做这个综合项目时,都能看到他们眼睛一亮——前面学了一大堆零散的知识点,终于能串起来了。今天我们就拿计算器和记事本这两个经典项目开刀,把之前学的信号槽、布局、文件操作、事件处理这些东西,全部揉进一个完整的项目里。
项目一:简易计算器
先说说计算器。你别看它功能简单,里面坑可不少。我记得自己第一次写计算器时,被那个「连续运算」的逻辑绕晕了好几天。
核心功能拆解
一个计算器,说白了就三件事:
- 输入数字:点击按钮,把数字显示到屏幕上
- 选择运算符:记住用户按了哪个运算符
- 计算结果:按等号时,把前面存的值和当前值做运算
嗯,这里要注意:用户可能会连续按运算符,比如「5 + 3 - 2 × 4 ÷ 2」。你得处理好运算优先级,或者干脆从左到右依次计算(简单计算器常用方式)。
界面布局
我个人习惯用 QGridLayout 来排按钮。0-9 数字键、加减乘除、等号、清空,一共 16 个按钮,4×4 的网格刚刚好。
// 创建按钮网格
QGridLayout *grid = new QGridLayout;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
QPushButton *btn = new QPushButton(buttonTexts[i][j]);
grid->addWidget(btn, i, j);
}
}
你想想看,如果不用布局,手动调坐标,窗口一拉伸就全乱套了。布局就是干这个的——自动适应窗口大小变化。
信号槽连接
每个按钮点击后,都要触发对应的槽函数。这里有个小技巧:把所有数字按钮的 clicked 信号连到同一个槽,然后通过 sender() 获取是哪个按钮被按了。
void Calculator::onDigitClicked() {
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
QString digit = btn->text();
// 追加到当前输入
currentInput += digit;
display->setText(currentInput);
}
我在项目中遇到过类似场景:一个界面有几十个按钮,如果每个按钮单独写一个槽,代码会膨胀到没法看。用 sender() 统一处理,清爽多了。
运算逻辑
计算器的核心逻辑其实是个状态机。我画个图帮你理解:
这个状态机搞清楚了,代码写起来就顺了。每个状态对应一个成员变量,记录当前处于哪个阶段。
核心变量:
double leftOperand:左操作数(运算符左边的值)QString operator:当前运算符bool waitingForOperand:是否在等待输入第二个数
项目二:记事本
记事本这个项目,我更喜欢拿来做教学。为什么?因为它涉及的知识面更广——文件读写、文本编辑、菜单栏、工具栏、快捷键,几乎把 Qt 的基础组件全用上了。
界面结构
一个标准的记事本,从上到下是:菜单栏 → 工具栏 → 编辑区 → 状态栏。用 QMainWindow 自带的布局就能搞定。
// 创建菜单栏
QMenu *fileMenu = menuBar()->addMenu("文件(&F)");
QAction *openAction = fileMenu->addAction("打开(&O)");
openAction->setShortcut(QKeySequence::Open);
// 创建工具栏
QToolBar *toolBar = addToolBar("文件");
toolBar->addAction(openAction);
// 创建编辑区
QTextEdit *editor = new QTextEdit;
setCentralWidget(editor);
// 状态栏
statusBar()->showMessage("就绪");
你看,Qt 把这些东西封装得特别好。菜单、工具栏、快捷键,几行代码就串起来了。我曾经在做一个文本编辑器项目时,光快捷键就配了二十多个,全靠 QAction 的 setShortcut 搞定。
文件读写
文件操作是记事本的核心。打开文件用 QFileDialog::getOpenFileName,保存用 QFileDialog::getSaveFileName。读写用 QFile 配合 QTextStream。
void Notepad::openFile() {
QString fileName = QFileDialog::getOpenFileName(this, "打开文件", QString(), "文本文件 (*.txt);;所有文件 (*)");
if (fileName.isEmpty()) return;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, "错误", "无法打开文件:" + file.errorString());
return;
}
QTextStream in(&file);
editor->setPlainText(in.readAll());
file.close();
statusBar()->showMessage("已打开:" + fileName);
}
注意:文件打开后一定要检查是否成功。我曾经在项目里忘了检查 file.open() 的返回值,结果用户选了个只读文件,程序直接崩溃了。从那以后,我养成了「打开必检查」的习惯。
编辑功能
除了基本的输入,记事本还可以加一些实用功能:
- 查找替换:用
QTextEdit::find()实现 - 字体设置:用
QFontDialog让用户选字体 - 自动换行:
editor->setLineWrapMode(QTextEdit::WidgetWidth) - 撤销/重做:
QTextEdit自带undo()和redo()
你想想看,这些功能如果让你从头实现,得写多少代码?Qt 直接帮你封装好了,你只需要调用几个函数就行。
综合对比
这两个项目,我建议你都做一遍。计算器侧重逻辑和状态管理,记事本侧重界面和文件操作。做完之后,你对 Qt 的理解会上一个台阶。
| 对比项 | 计算器 | 记事本 |
|---|---|---|
| 核心知识点 | 信号槽、布局、状态机 | 菜单栏、文件IO、文本编辑 |
| 难点 | 运算逻辑、连续运算 | 文件编码、查找替换 |
| 推荐学习时长 | 2-3天 | 3-4天 |
| 实用价值 | 理解事件驱动编程 | 掌握桌面应用基本框架 |
我的建议:先做计算器,再做记事本。计算器代码量小(大概200-300行),适合练手。记事本代码量大一些(500-800行),但结构更清晰,适合巩固。
好了,这两个项目就讲到这里。代码我已经给了核心部分,剩下的细节你边写边查文档就行。记住,编程这东西,光看是学不会的,一定要动手敲一遍。