Day3 教学示例:增强版计算器接入 + QMainWindow/QSS 两版参考实现

- calculator_designer_ext(Day2 增强版:表达式求值 + 括号,递归下降解析器)接入 cmake
- calculator_mainwindow(Day3 上午):增强版计算器装进 QMainWindow,
  菜单/状态栏/历史停靠窗,纯代码 + 显式 connect,与 mainwindow_showcase 形成坑卡对照
- calculator_mainwindow_qss(Day3 下午):上午版 + QSS 双主题美化(按键分色/伪状态/深浅切换)
- 验证 mainwindow_showcase(QMainWindow 六大组件综合演示,.ui + .qrc)
- OUTLINE_4DAY.md / ASSIGNMENTS.md Day3 衔接增强版起点 + 参考实现指引
- 补提交 Day2 calculator/ 与 calculator_designer/ 参考实现(CMakeLists 已引用)
- 全量构建 28/28,4 目标离屏自测退出码 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
张宗平
2026-07-07 22:59:05 +08:00
parent ea6f12e7ac
commit 05a685c952
45 changed files with 4599 additions and 6 deletions
@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.5)
project(Calculator LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(Qt5 COMPONENTS Widgets REQUIRED)
if(ANDROID)
add_library(Calculator SHARED
main.cpp
calculatordesigner.h
calculatordesigner.cpp
expressionevaluator.h
expressionevaluator.cpp
widget.ui
)
else()
add_executable(Calculator
main.cpp
calculatordesigner.h
calculatordesigner.cpp
expressionevaluator.h
expressionevaluator.cpp
widget.ui
)
endif()
target_link_libraries(Calculator PRIVATE Qt5::Widgets)
@@ -0,0 +1,229 @@
#include "calculatordesigner.h"
#include <QPushButton>
#include "expressionevaluator.h"
#include "ui_widget.h"
namespace {
bool isOperatorChar(QChar ch) {
return ch == QLatin1Char('+') || ch == QLatin1Char('-') ||
ch == QLatin1Char('*') || ch == QLatin1Char('/');
}
bool isDigitOrDotChar(QChar ch) {
return ch.isDigit() || ch == QLatin1Char('.');
}
} // namespace
CalculatorDesigner::CalculatorDesigner(QWidget* parent)
: QWidget(parent), ui(new Ui::CalculatorForm) {
ui->setupUi(this); // 表单里的控件树 + QGridLayout 全部由 Designer XML 建好
connect(ui->btn0, &QPushButton::clicked, this, [this] { onDigit(0); });
connect(ui->btn1, &QPushButton::clicked, this, [this] { onDigit(1); });
connect(ui->btn2, &QPushButton::clicked, this, [this] { onDigit(2); });
connect(ui->btn3, &QPushButton::clicked, this, [this] { onDigit(3); });
connect(ui->btn4, &QPushButton::clicked, this, [this] { onDigit(4); });
connect(ui->btn5, &QPushButton::clicked, this, [this] { onDigit(5); });
connect(ui->btn6, &QPushButton::clicked, this, [this] { onDigit(6); });
connect(ui->btn7, &QPushButton::clicked, this, [this] { onDigit(7); });
connect(ui->btn8, &QPushButton::clicked, this, [this] { onDigit(8); });
connect(ui->btn9, &QPushButton::clicked, this, [this] { onDigit(9); });
connect(ui->btnDot, &QPushButton::clicked, this, [this] { onDot(); });
connect(ui->btnPlus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('+')); });
connect(ui->btnMinus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('-')); });
connect(ui->btnMul, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('*')); });
connect(ui->btnDiv, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('/')); });
connect(ui->btnLeftParen, &QPushButton::clicked, this, &CalculatorDesigner::onLeftParen);
connect(ui->btnRightParen, &QPushButton::clicked, this, &CalculatorDesigner::onRightParen);
connect(ui->btnEquals, &QPushButton::clicked, this, &CalculatorDesigner::onEquals);
connect(ui->btnClear, &QPushButton::clicked, this, &CalculatorDesigner::onClear);
connect(ui->btnBackspace, &QPushButton::clicked, this, &CalculatorDesigner::onBackspace);
onClear();
}
CalculatorDesigner::~CalculatorDesigner() { delete ui; }
void CalculatorDesigner::onDigit(int digit) {
if (m_error) onClear();
if (m_resultShown) {
m_expression.clear();
m_openParenCount = 0;
m_resultShown = false;
}
const QString token = currentOperandToken();
if (token == QStringLiteral("0")) {
m_expression.chop(1); // 用新数字替换孤立的前导 0,避免出现 "007"
} else if (currentOperandDigitCount() >= kMaxOperandLength) {
return; // 达到单个运算数的长度上限,忽略后续数字
}
if (m_expression.size() >= kMaxExpressionLength) return;
m_expression.append(QString::number(digit));
refreshDisplays();
}
void CalculatorDesigner::onDot() {
if (m_error) onClear();
if (m_resultShown) {
m_expression.clear();
m_openParenCount = 0;
m_resultShown = false;
}
const QString token = currentOperandToken();
if (token.contains(QLatin1Char('.'))) return; // 当前运算数已有小数点
const int appended = token.isEmpty() ? 2 : 1; // 空token时需要补一个前导 0
if (m_expression.size() + appended > kMaxExpressionLength) return;
if (token.isEmpty()) m_expression.append(QLatin1Char('0'));
m_expression.append(QLatin1Char('.'));
refreshDisplays();
}
void CalculatorDesigner::onOperator(QChar op) {
if (m_error) return;
if (m_resultShown) {
m_expression = m_lastResultText;
m_resultShown = false;
}
if (m_expression.isEmpty()) {
if (op != QLatin1Char('-')) return; // 表达式开头只允许负号(单目负号)
} else {
const QChar last = m_expression.at(m_expression.size() - 1);
if (last == QLatin1Char('(')) {
if (op != QLatin1Char('-')) return; // 左括号后只允许负号
} else if (isOperatorChar(last)) {
m_expression.chop(1); // 连续按运算符时,用新的运算符替换上一个
}
}
if (m_expression.size() >= kMaxExpressionLength) return;
m_expression.append(op);
refreshDisplays();
}
void CalculatorDesigner::onLeftParen() {
if (m_error) onClear();
if (m_resultShown) {
m_expression.clear();
m_openParenCount = 0;
m_resultShown = false;
}
if (!m_expression.isEmpty()) {
const QChar last = m_expression.at(m_expression.size() - 1);
if (!isOperatorChar(last) && last != QLatin1Char('(')) {
return; // 数字或右括号后不支持隐式乘法,忽略
}
}
if (m_expression.size() >= kMaxExpressionLength) return;
m_expression.append(QLatin1Char('('));
++m_openParenCount;
refreshDisplays();
}
void CalculatorDesigner::onRightParen() {
if (m_error) return;
if (m_resultShown) return;
if (m_openParenCount <= 0 || m_expression.isEmpty()) return;
const QChar last = m_expression.at(m_expression.size() - 1);
if (isOperatorChar(last) || last == QLatin1Char('(')) return; // 括号内需要完整子表达式
if (m_expression.size() >= kMaxExpressionLength) return;
m_expression.append(QLatin1Char(')'));
--m_openParenCount;
refreshDisplays();
}
void CalculatorDesigner::onEquals() {
if (m_error) return;
if (m_expression.isEmpty()) return;
QString toEvaluate = m_expression;
for (int i = 0; i < m_openParenCount; ++i) toEvaluate.append(QLatin1Char(')')); // 自动补全未闭合括号
const ExpressionEvaluator::Result result = ExpressionEvaluator::evaluate(toEvaluate);
if (!result.ok) {
showError();
return;
}
const QString resultText = QString::number(result.value, 'g', ExpressionEvaluator::kResultPrecision);
m_expression = toEvaluate;
m_lastResultText = resultText;
m_openParenCount = 0;
m_resultShown = true;
ui->expressionEdit->setPlainText(m_expression + QStringLiteral(" = ") + resultText);
ui->display->setText(resultText);
}
void CalculatorDesigner::onClear() {
m_expression.clear();
m_lastResultText.clear();
m_openParenCount = 0;
m_resultShown = false;
m_error = false;
ui->expressionEdit->clear();
ui->display->setText(QStringLiteral("0"));
}
void CalculatorDesigner::onBackspace() {
if (m_error) { onClear(); return; }
if (m_resultShown) return; // 结果已展示,需先清空或继续输入新表达式
if (m_expression.isEmpty()) return;
const QChar removed = m_expression.at(m_expression.size() - 1);
m_expression.chop(1);
if (removed == QLatin1Char('(')) --m_openParenCount;
else if (removed == QLatin1Char(')')) ++m_openParenCount;
refreshDisplays();
}
QString CalculatorDesigner::displayText() const { return ui->display->text(); }
QString CalculatorDesigner::expressionText() const { return ui->expressionEdit->toPlainText(); }
QString CalculatorDesigner::currentOperandToken() const {
int start = m_expression.size();
while (start > 0 && isDigitOrDotChar(m_expression.at(start - 1))) --start;
return m_expression.mid(start);
}
int CalculatorDesigner::currentOperandDigitCount() const {
const QString token = currentOperandToken();
int count = 0;
for (const QChar ch : token) {
if (ch.isDigit()) ++count;
}
return count;
}
QString CalculatorDesigner::lastNumberToken() const {
int end = m_expression.size();
while (end > 0 && !isDigitOrDotChar(m_expression.at(end - 1))) --end; // 跳过末尾的运算符/括号
int start = end;
while (start > 0 && isDigitOrDotChar(m_expression.at(start - 1))) --start;
return m_expression.mid(start, end - start);
}
void CalculatorDesigner::refreshDisplays() {
ui->expressionEdit->setPlainText(m_expression);
const QString token = lastNumberToken();
ui->display->setText(token.isEmpty() ? QStringLiteral("0") : token);
}
void CalculatorDesigner::showError() {
ui->display->setText(QStringLiteral("Error"));
m_error = true;
}
@@ -0,0 +1,52 @@
#pragma once
#include <QChar>
#include <QString>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class CalculatorForm;
}
QT_END_NAMESPACE
class CalculatorDesigner : public QWidget {
Q_OBJECT
public:
explicit CalculatorDesigner(QWidget* parent = nullptr);
~CalculatorDesigner() override;
// 供自测直接调用,效果等价于点击对应按钮;逻辑与 ../calculator/ 版一字不差。
void onDigit(int digit);
void onDot();
void onOperator(QChar op);
void onLeftParen();
void onRightParen();
void onEquals();
void onClear();
void onBackspace();
QString displayText() const;
QString expressionText() const;
private:
// 单个运算数(数字串)允许的最大长度,要求不得小于 10 位。
static constexpr int kMaxOperandLength = 15;
static_assert(kMaxOperandLength >= 10, "单个运算数的最大长度不得小于 10 位");
// 表达式总长度上限,要求不得低于 100 个字符。
static constexpr int kMaxExpressionLength = 200;
static_assert(kMaxExpressionLength >= 100, "表达式总长度上限不得低于 100 个字符");
QString currentOperandToken() const;
int currentOperandDigitCount() const;
QString lastNumberToken() const;
void refreshDisplays();
void showError();
Ui::CalculatorForm* ui;
QString m_expression; // 当前正在编辑的表达式,例如 "12+3*(4-2"
QString m_lastResultText; // 上一次成功计算得到的结果文本
int m_openParenCount = 0; // 尚未闭合的左括号数量
bool m_resultShown = false; // 显示区当前展示的是计算结果而非正在输入的数字
bool m_error = false;
};
@@ -0,0 +1,144 @@
#include "expressionevaluator.h"
namespace {
// 递归下降解析器:expression := term (('+'|'-') term)*
// term := factor (('*'|'/') factor)*
// factor := ('+'|'-') factor | '(' expression ')' | number
// 括号会先被完整求值,天然实现"优先级提升";乘除法在 term 层处理,
// 天然高于 expression 层的加减法。
class Parser {
public:
explicit Parser(const QString& text) : m_text(text) {}
ExpressionEvaluator::Result parse() {
ExpressionEvaluator::Result result;
skipSpaces();
if (atEnd()) {
result.errorMessage = QStringLiteral("表达式为空");
return result;
}
bool ok = true;
const double value = parseExpression(ok);
skipSpaces();
if (!ok) {
result.errorMessage = m_error;
return result;
}
if (!atEnd()) {
result.errorMessage = QStringLiteral("表达式包含无法识别的字符");
return result;
}
result.ok = true;
result.value = value;
return result;
}
private:
double parseExpression(bool& ok) {
double value = parseTerm(ok);
while (ok) {
skipSpaces();
if (peek() == QLatin1Char('+')) {
advance();
value += parseTerm(ok);
} else if (peek() == QLatin1Char('-')) {
advance();
value -= parseTerm(ok);
} else {
break;
}
}
return value;
}
double parseTerm(bool& ok) {
double value = parseFactor(ok);
while (ok) {
skipSpaces();
if (peek() == QLatin1Char('*')) {
advance();
value *= parseFactor(ok);
} else if (peek() == QLatin1Char('/')) {
advance();
const double rhs = parseFactor(ok);
if (!ok) return 0.0;
if (rhs == 0.0) {
fail(QStringLiteral("除数不能为零"), ok);
return 0.0;
}
value /= rhs;
} else {
break;
}
}
return value;
}
double parseFactor(bool& ok) {
skipSpaces();
if (peek() == QLatin1Char('+')) {
advance();
return parseFactor(ok);
}
if (peek() == QLatin1Char('-')) {
advance();
return -parseFactor(ok);
}
if (peek() == QLatin1Char('(')) {
advance();
const double value = parseExpression(ok);
if (!ok) return 0.0;
skipSpaces();
if (peek() != QLatin1Char(')')) {
fail(QStringLiteral("括号不匹配"), ok);
return 0.0;
}
advance();
return value;
}
return parseNumber(ok);
}
double parseNumber(bool& ok) {
skipSpaces();
const int start = m_pos;
bool hasDigits = false;
while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
if (!atEnd() && peek() == QLatin1Char('.')) {
advance();
while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
}
if (!hasDigits) {
fail(QStringLiteral("表达式格式错误,缺少数字"), ok);
return 0.0;
}
const QString token = m_text.mid(start, m_pos - start);
bool numOk = false;
const double value = token.toDouble(&numOk);
if (!numOk) {
fail(QStringLiteral("数字格式错误"), ok);
return 0.0;
}
return value;
}
void skipSpaces() { while (!atEnd() && peek().isSpace()) advance(); }
bool atEnd() const { return m_pos >= m_text.size(); }
QChar peek() const { return atEnd() ? QChar() : m_text.at(m_pos); }
void advance() { ++m_pos; }
void fail(const QString& message, bool& ok) { ok = false; m_error = message; }
const QString& m_text;
int m_pos = 0;
QString m_error;
};
} // namespace
ExpressionEvaluator::Result ExpressionEvaluator::evaluate(const QString& expression) {
Parser parser(expression);
return parser.parse();
}
@@ -0,0 +1,20 @@
#pragma once
#include <QString>
// 对形如 "1+2*(3-4)" 的中缀表达式求值:
// 乘除优先级高于加减,括号可提升优先级。
class ExpressionEvaluator {
public:
struct Result {
bool ok = false;
double value = 0.0;
QString errorMessage;
};
// 结果显示精度(有效数字位数),要求不得小于 10。
static constexpr int kResultPrecision = 12;
static_assert(kResultPrecision >= 10, "结果显示精度不得小于 10 位有效数字");
static Result evaluate(const QString& expression);
};
@@ -0,0 +1,93 @@
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include "calculatordesigner.h"
// ------------------------------------------------------------ 自测入口 ----
// 与 ../calculator/qt_calculator.cpp 完全相同的 5 组用例:验证「界面搭建方式
// 换了,行为不变」。
static bool expectEqual(const char* label, const QString& actual, const QString& expected) {
if (actual != expected) {
qCritical() << "自测失败:" << label << "实际=" << actual << "预期=" << expected;
return false;
}
qInfo() << "自测通过:" << label << "=>" << actual;
return true;
}
static bool runSelfTest(CalculatorDesigner* calc) {
calc->onClear();
calc->onDigit(7); calc->onOperator(QLatin1Char('+')); calc->onDigit(3); calc->onEquals();
if (!expectEqual("7 + 3 =", calc->displayText(), QStringLiteral("10"))) return false;
calc->onClear();
calc->onDigit(6); calc->onOperator(QLatin1Char('/')); calc->onDigit(0); calc->onEquals();
if (!expectEqual("6 ÷ 0 =", calc->displayText(), QStringLiteral("Error"))) return false;
calc->onClear();
calc->onDigit(4); calc->onDot(); calc->onDigit(5);
calc->onOperator(QLatin1Char('+'));
calc->onDigit(0); calc->onDot(); calc->onDigit(5);
calc->onEquals();
if (!expectEqual("4.5 + 0.5 =", calc->displayText(), QStringLiteral("5"))) return false;
calc->onClear();
calc->onDigit(1); calc->onDigit(2); calc->onBackspace();
if (!expectEqual("12 退格", calc->displayText(), QStringLiteral("1"))) return false;
calc->onClear();
calc->onDigit(2); calc->onOperator(QLatin1Char('+'));
calc->onDigit(3); calc->onOperator(QLatin1Char('+'));
calc->onDigit(4); calc->onEquals();
if (!expectEqual("2 + 3 + 4 =(链式运算)", calc->displayText(), QStringLiteral("9"))) return false;
calc->onClear();
calc->onDigit(2); calc->onOperator(QLatin1Char('+'));
calc->onDigit(3); calc->onOperator(QLatin1Char('*'));
calc->onDigit(4); calc->onEquals();
if (!expectEqual("2 + 3 × 4 =(乘法优先于加法)", calc->displayText(), QStringLiteral("14"))) return false;
calc->onClear();
calc->onLeftParen();
calc->onDigit(2); calc->onOperator(QLatin1Char('+')); calc->onDigit(3);
calc->onRightParen();
calc->onOperator(QLatin1Char('*'));
calc->onDigit(4); calc->onEquals();
if (!expectEqual("(2 + 3) × 4 =(括号提升优先级)", calc->displayText(), QStringLiteral("20"))) return false;
if (!expectEqual("(2 + 3) × 4 = 表达式区文本", calc->expressionText(), QStringLiteral("(2+3)*4 = 20"))) return false;
calc->onClear();
calc->onDigit(1); calc->onOperator(QLatin1Char('+'));
calc->onLeftParen();
calc->onDigit(2); calc->onOperator(QLatin1Char('*'));
calc->onLeftParen();
calc->onDigit(3); calc->onOperator(QLatin1Char('+')); calc->onDigit(4);
calc->onRightParen();
calc->onRightParen();
calc->onEquals();
if (!expectEqual("1 + (2 × (3 + 4)) =(嵌套括号)", calc->displayText(), QStringLiteral("15"))) return false;
calc->onClear();
calc->onLeftParen();
calc->onDigit(1); calc->onOperator(QLatin1Char('+')); calc->onDigit(2);
calc->onEquals(); // 未手动补齐右括号,应自动闭合后求值
if (!expectEqual("(1 + 2 =(自动补全括号)", calc->displayText(), QStringLiteral("3"))) return false;
return true;
}
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
CalculatorDesigner calc;
calc.show();
if (qgetenv("QT_QPA_PLATFORM") == "offscreen") {
QTimer::singleShot(0, &calc, [&calc] {
const bool ok = runSelfTest(&calc);
QCoreApplication::exit(ok ? 0 : 1);
});
}
return app.exec();
}
@@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalculatorForm</class>
<widget class="QWidget" name="CalculatorForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>简易计算器(Designer 版)</string>
</property>
<layout class="QVBoxLayout" name="rootLayout">
<item>
<widget class="QTextEdit" name="expressionEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="display">
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="keypadLayout">
<item row="0" column="0">
<widget class="QPushButton" name="btn7">
<property name="text">
<string>7</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="btn8">
<property name="text">
<string>8</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btn9">
<property name="text">
<string>9</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="btnDiv">
<property name="text">
<string>÷</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="btn4">
<property name="text">
<string>4</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="btn5">
<property name="text">
<string>5</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btn6">
<property name="text">
<string>6</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="btnMul">
<property name="text">
<string>×</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="btn1">
<property name="text">
<string>1</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="btn2">
<property name="text">
<string>2</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btn3">
<property name="text">
<string>3</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="btnMinus">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="btn0">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="btnDot">
<property name="text">
<string>.</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnEquals">
<property name="text">
<string>=</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="btnPlus">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="btnClear">
<property name="text">
<string>C</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="btnBackspace">
<property name="text">
<string>⌫</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="btnLeftParen">
<property name="text">
<string>(</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="btnRightParen">
<property name="text">
<string>)</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>