qml简易计算器
用 Qt6 + QML 写一个跨平台计算器:EasyCalculator 项目实践
本文介绍个人开源项目 EasyCalculator 的设计思路与实现细节。项目使用 Qt 6.4+、QML 与 C++ 构建,界面简洁,逻辑清晰,适合作为 QML 与 C++/QML 混合开发的入门示例。
一、项目简介
EasyCalculator 是一款桌面计算器应用,支持 Windows、Linux、macOS。界面采用浅灰背景与网格按钮布局,交互风格接近系统自带计算器。
功能一览
| 类别 | 功能 |
|---|---|
| 四则运算 | 加、减、乘、除 |
| 单目运算 | 百分比 %、平方 x²、平方根 √x、倒数 x⁻¹、正负号 +/- |
| 编辑操作 | 全清 C、清当前项 CE、退格 ⌫、小数点 . |
界面分为两块显示区:
- 过程区:显示如
12 +的运算过程; - 结果区:显示当前输入或计算结果。
项目仓库:https://github.com/xcj-fighting/EasyCalculator

二、技术选型
| 技术 | 作用 |
|---|---|
| Qt 6.4+ | 跨平台 GUI 框架 |
| QML | 声明式 UI,快速布局与样式 |
| C++ | 计算逻辑、状态管理 |
| CMake 3.20+ | 构建与 QML 模块打包 |
选择 QML 负责界面、C++ 负责逻辑 的分层方式,是 Qt 现代应用开发的常见模式:UI 迭代快,核心算法可单测、可复用。
三、项目结构
EasyCalculator/
├── main.cpp # 程序入口,加载 QML
├── calculator.h/.cpp # 计算器核心逻辑
├── Main.qml # 主窗口与按钮网格
├── buttons/
│ ├── NumberButton.qml # 数字键样式
│ ├── FunctionalButton.qml # 功能键样式
│ └── EqualSignButton.qml # 等号键(强调色)
├── CMakeLists.txt
└── README.md
构建产物通过 qt_add_qml_module 将 QML 与 C++ 注册为同一模块 EasyCalculator,主界面通过 import EasyCalculator 直接使用 Calculator 类型。
四、架构设计:前后端如何协作
整体数据流可以概括为:
用户点击按钮 → QML 调用 Calculator 方法 → C++ 更新状态 → 属性变化 → QML 界面自动刷新
4.1 C++ 侧:暴露给 QML 的 Calculator 类
Calculator 继承 QObject,通过宏与注解注册到 QML 模块:
QML_ELEMENT:在 QML 中可直接Calculator { }实例化;Q_PROPERTY:绑定processText、resultText到界面文本;Q_INVOKABLE:供 QML 调用的槽函数(输入数字、运算符、计算等)。
头文件核心声明如下:
class Calculator : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString processText READ processText WRITE setProcessText NOTIFY processTextChanged)
Q_PROPERTY(QString resultText READ resultText WRITE setResultText NOTIFY resultTextChanged)
public:
Q_INVOKABLE void inputNumber(const QString &number);
Q_INVOKABLE void inputOperator(const QString &op);
Q_INVOKABLE void calculate();
Q_INVOKABLE void clear();
Q_INVOKABLE void clearEntry();
Q_INVOKABLE void toggleSign();
Q_INVOKABLE void backspace();
Q_INVOKABLE void percent();
Q_INVOKABLE void square();
Q_INVOKABLE void squareRoot();
Q_INVOKABLE void reciprocal();
// ...
};
4.2 状态机思路
计算器内部用少量成员变量维护状态:
| 成员 | 含义 |
|---|---|
m_resultText |
当前显示的数字 |
m_processText |
过程区文案 |
m_previousValue |
上一个操作数 |
m_operator |
当前运算符(+ - × ÷) |
m_waitingForNewValue |
是否等待输入下一个操作数 |
输入数字时:若处于「等待新值」状态,则覆盖显示;否则在末尾拼接,并防止重复小数点。
输入运算符时:保存当前值为 m_previousValue,过程区显示 前值 + 运算符,结果区重置为 0,并置 m_waitingForNewValue = true。
按下等号时:根据 m_operator 做四则运算;除数为 0 时显示「错误」;完成后将结果写回,并允许继续以结果为起点运算。
4.3 四则运算与错误处理示例
void Calculator::calculate()
{
if (m_operator.isEmpty() || m_previousValue.isEmpty()) return;
double previousValue = m_previousValue.toDouble();
double currentValue = m_resultText.toDouble();
m_processText += QString(" %1 =").arg(m_resultText);
if (m_operator == "+") {
m_resultText = QString::number(previousValue + currentValue);
} else if (m_operator == "-") {
m_resultText = QString::number(previousValue - currentValue);
} else if (m_operator == "×") {
m_resultText = QString::number(previousValue * currentValue);
} else if (m_operator == "÷") {
if (currentValue == 0.0) {
m_resultText = "错误";
} else {
m_resultText = QString::number(previousValue / currentValue);
}
}
emit resultTextChanged(m_resultText);
emit processTextChanged(m_processText);
m_operator = "";
m_previousValue = m_resultText;
m_waitingForNewValue = true;
}
单目函数如平方根会对负数返回「错误」,倒数在除数为 0 时直接忽略,与常见计算器行为一致。
五、QML 界面实现
5.1 主窗口 Main.qml
主界面使用 ColumnLayout + GridLayout(4 列)组织显示区与按钮区。在根节点下声明 C++ 对象:
Calculator {
id: calculator
}
过程与结果通过属性绑定自动更新:
Text {
text: calculator.processText
// ...
}
Text {
text: calculator.resultText
font.pixelSize: 44
// ...
}
按钮点击只需一行绑定,例如:
NumberButton {
text: "7"
onClicked: calculator.inputNumber("7")
}
FunctionalButton {
text: "÷"
onClicked: calculator.inputOperator("÷")
}
EqualSignButton {
text: "="
onClicked: calculator.calculate()
}
窗口固定最小尺寸 328×505,背景色 #f4f2f2,整体偏简洁桌面风格。
5.2 可复用按钮组件
三种按钮组件结构相同,通过 颜色主题 区分用途:
| 组件 | 背景风格 | 用途 |
|---|---|---|
NumberButton |
白底 | 数字、+/-、. |
FunctionalButton |
浅灰底 | %、CE、C、运算符等 |
EqualSignButton |
蓝色 #0067c0 |
等号,视觉强调 |
每个按钮基于 Rectangle + MouseArea,开启 hoverEnabled,在悬浮与按下时切换背景色与文字色,无需依赖 Button 控件即可获得细腻反馈。
组件内预设 Layout.minimumWidth/Height 与 fillWidth/fillHeight,便于在 GridLayout 中均匀拉伸。
六、程序入口与 CMake 配置
6.1 main.cpp
使用 QQmlApplicationEngine 加载编译进资源的 QML:
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/EasyCalculator/Main.qml"_qs);
engine.load(url);
return app.exec();
若 QML 加载失败,通过 objectCreated 信号退出进程,避免静默失败。
6.2 CMakeLists.txt 要点
find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)
qt_add_executable(EasyCalculator main.cpp)
qt_add_qml_module(EasyCalculator
URI EasyCalculator
VERSION 1.0
QML_FILES
Main.qml
buttons/EqualSignButton.qml
buttons/FunctionalButton.qml
buttons/NumberButton.qml
SOURCES
calculator.h
calculator.cpp
)
target_link_libraries(EasyCalculator PRIVATE Qt6::Quick)
qt_add_qml_module 一步完成:QML 文件打包、类型注册、Calculator 与 QML 的链接。这是 Qt 6 推荐的新式 QML 工程写法,比手动维护 .qrc 更清晰。

浙公网安备 33010602011771号