「Qt Widget中文示例指南」如何创建一个计算器?(二)

news2025/1/10 20:32:50

Qt 是目前最先进、最完整的跨平台C++开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。

本文将展示如何使用信号和槽来实现计算器小部件的功能,以及如何使用QGridLayout在网格中放置子小部件。在上文中(点击这里回顾>>)为大家介绍了实现计算器的Calculator类定义,本文将主要介绍Calculator类是如何实现的,持续关注我们哦~

点击获取Qt Widget组件下载(Q技术交流:166830288)

Qt Widget中文示例指南图集

计算器示例的屏幕截图

这个例子由两个类组成:

  • Calculator是计算器小部件,具有计算器的所有功能。
  • Button是用于每个计算器按钮的小部件,它派生自QToolButton。

我们将从回顾计算器开始,然后再看看按钮。

Calculator类实现
Calculator::Calculator(QWidget *parent)
: QWidget(parent), sumInMemory(0.0), sumSoFar(0.0)
, factorSoFar(0.0), waitingForOperand(true)
{

在构造函数中,初始化计算器的状态。pendingAdditiveOperator和pendingMultiplicativeOperator变量不需要显式初始化,因为QString构造函数将它们初始化为空字符串。也可以直接在header文件中初始化这些变量,这称为成员初始化,避免了长初始化列表。

display = new QLineEdit("0");
display->setReadOnly(true);
display->setAlignment(Qt::AlignRight);
display->setMaxLength(15);

QFont font = display->font();
font.setPointSize(font.pointSize() + 8);
display->setFont(font);

我们创建了表示计算器显示的QLineEdit ,并设置了它的一些属性,特别地我们将其设置为只读。

我们还将显示器的字体放大了8个点。

for (int i = 0; i < NumDigitButtons; ++i)
digitButtons[i] = createButton(QString::number(i), &Calculator::digitClicked);

Button *pointButton = createButton(tr("."), &Calculator::pointClicked);
Button *changeSignButton = createButton(tr("\302\261"), &Calculator::changeSignClicked);

Button *backspaceButton = createButton(tr("Backspace"), &Calculator::backspaceClicked);
Button *clearButton = createButton(tr("Clear"), &Calculator::clear);
Button *clearAllButton = createButton(tr("Clear All"), &Calculator::clearAll);

Button *clearMemoryButton = createButton(tr("MC"), &Calculator::clearMemory);
Button *readMemoryButton = createButton(tr("MR"), &Calculator::readMemory);
Button *setMemoryButton = createButton(tr("MS"), &Calculator::setMemory);
Button *addToMemoryButton = createButton(tr("M+"), &Calculator::addToMemory);

Button *divisionButton = createButton(tr("\303\267"), &Calculator::multiplicativeOperatorClicked);
Button *timesButton = createButton(tr("\303\227"), &Calculator::multiplicativeOperatorClicked);
Button *minusButton = createButton(tr("-"), &Calculator::additiveOperatorClicked);
Button *plusButton = createButton(tr("+"), &Calculator::additiveOperatorClicked);

Button *squareRootButton = createButton(tr("Sqrt"), &Calculator::unaryOperatorClicked);
Button *powerButton = createButton(tr("x\302\262"), &Calculator::unaryOperatorClicked);
Button *reciprocalButton = createButton(tr("1/x"), &Calculator::unaryOperatorClicked);
Button *equalButton = createButton(tr("="), &Calculator::equalClicked);

对于每个按钮,我们使用适当的文本标签和连接到按钮的插槽调用私有createButton()函数。

QGridLayout *mainLayout = new QGridLayout;
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(display, 0, 0, 1, 6);
mainLayout->addWidget(backspaceButton, 1, 0, 1, 2);
mainLayout->addWidget(clearButton, 1, 2, 1, 2);
mainLayout->addWidget(clearAllButton, 1, 4, 1, 2);

mainLayout->addWidget(clearMemoryButton, 2, 0);
mainLayout->addWidget(readMemoryButton, 3, 0);
mainLayout->addWidget(setMemoryButton, 4, 0);
mainLayout->addWidget(addToMemoryButton, 5, 0);

for (int i = 1; i < NumDigitButtons; ++i) {
int row = ((9 - i) / 3) + 2;
int column = ((i - 1) % 3) + 1;
mainLayout->addWidget(digitButtons[i], row, column);
}

mainLayout->addWidget(digitButtons[0], 5, 1);
mainLayout->addWidget(pointButton, 5, 2);
mainLayout->addWidget(changeSignButton, 5, 3);

mainLayout->addWidget(divisionButton, 2, 4);
mainLayout->addWidget(timesButton, 3, 4);
mainLayout->addWidget(minusButton, 4, 4);
mainLayout->addWidget(plusButton, 5, 4);

mainLayout->addWidget(squareRootButton, 2, 5);
mainLayout->addWidget(powerButton, 3, 5);
mainLayout->addWidget(reciprocalButton, 4, 5);
mainLayout->addWidget(equalButton, 5, 5);
setLayout(mainLayout);

setWindowTitle(tr("Calculator"));
}

布局由单个QGridLayout处理,QLayout::setSizeConstraint()调用确保Calculator小部件始终显示为其最佳大小(其大小提示),从而防止用户调整计算器的大小,大小提示由子小部件的大小和大小策略决定。

大多数子部件只占用网格布局中的一个单元格,对于这些我们只需要将一行和一列传递给QGridLayout::addWidget()。display、backspaceButton、clearButton和clearAllButton小部件占用多于一列,对于这些我们还必须船体一个行空间和一个列空间。

void Calculator::digitClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
int digitValue = clickedButton->text().toInt();
if (display->text() == "0" && digitValue == 0.0)
return;

if (waitingForOperand) {
display->clear();
waitingForOperand = false;
}
display->setText(display->text() + QString::number(digitValue));
}

按下计算器的数字按钮时将发出按钮的clicked()信号,该信号将触发digitClicked()插槽。

首先我们先使用QObject::sender()找出哪个按钮发送了信号,这个函数以QObject指针的形式返回发送方。因为我们知道发送方是一个Button对象,所以可以安全地强制转换QObject。本来可以使用C风格的强制转换或c++ static_cast<>(),但作为一种防御性编程技术,我们使用qobject_cast()。这样做的好处是,如果对象的类型错误,则返回空指针。空指针导致的崩溃比不安全强制转换导致的崩溃更容易诊断。有了按钮后,我们使用QToolButton::text()提取操作符。

插槽需要特别考虑两种情况,如果显示包含“0”,而用户点击了“0”按钮,那么显示“00”将是愚蠢的。如果计算器处于等待新操作数的状态,新数字就是新操作数的第一位;在这种情况下,必须首先清除先前计算的任何结果。

最后,我们将新数字附加到显示的值后面。

void Calculator::unaryOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();
double result = 0.0;

if (clickedOperator == tr("Sqrt")) {
if (operand < 0.0) {
abortOperation();
return;
}
result = std::sqrt(operand);
} else if (clickedOperator == tr("x\302\262")) {
result = std::pow(operand, 2.0);
} else if (clickedOperator == tr("1/x")) {
if (operand == 0.0) {
abortOperation();
return;
}
result = 1.0 / operand;
}
display->setText(QString::number(result));
waitingForOperand = true;
}

每当单击一个一元操作符按钮时,就调用unaryOperatorClicked()插槽,再次使用QObject::sender()获取指向被单击按钮的指针。操作符从按钮的文本中提取并存储在clickoperator中,操作数从display中获得。

然后我们执行这个操作,如果Sqrt应用于负数或1/x到零,调用abortOperation()。如果一切顺利,我们将在行编辑中显示操作的结果,并将waitingForOperand设置为true。这确保了如果用户键入一个新数字,该数字将被视为一个新的操作数,而不是附加到当前值。

void Calculator::additiveOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
if (!clickedButton)
return;
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();

当用户单击+或-按钮时调用additiveOperatorClicked()槽。

在对单击的操作符进行实际操作之前,我们必须处理所有挂起的操作。从乘法运算符开始,因为它们的优先级高于加法运算符:

if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
display->setText(QString::number(factorSoFar));
operand = factorSoFar;
factorSoFar = 0.0;
pendingMultiplicativeOperator.clear();
}

如果之前已经单击了x或÷,而没有随后单击=,则显示中的当前值是x或÷操作符的右操作数,我们最终可以执行该操作并更新显示。

if (!pendingAdditiveOperator.isEmpty()) {
if (!calculate(operand, pendingAdditiveOperator)) {
abortOperation();
return;
}
display->setText(QString::number(sumSoFar));
} else {
sumSoFar = operand;
}

如果前面已经单击了+或-,则sumSoFar是左操作数,而显示的当前值是操作符的右操作数。如果没有挂起的加法运算符,则简单地将sumSoFar设置为显示中的文本。

pendingAdditiveOperator = clickedOperator;
waitingForOperand = true;
}

最后,我们可以处理刚刚点击的操作符。由于还没有右操作数,所以将单击的操作符存储在pendingAdditiveOperator变量中。稍后当有一个右操作数,而左操作数为sumSoFar时,将应用该操作。

void Calculator::multiplicativeOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
if (!clickedButton)
return;
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();

if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
display->setText(QString::number(factorSoFar));
} else {
factorSoFar = operand;
}

pendingMultiplicativeOperator = clickedOperator;
waitingForOperand = true;
}

multiplicativeOperatorClicked()插槽类似于additiveOperatorClicked(),不需要担心挂起的加法运算符,因为乘法运算符优先于加法运算符。

void Calculator::equalClicked()
{
double operand = display->text().toDouble();

if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
operand = factorSoFar;
factorSoFar = 0.0;
pendingMultiplicativeOperator.clear();
}
if (!pendingAdditiveOperator.isEmpty()) {
if (!calculate(operand, pendingAdditiveOperator)) {
abortOperation();
return;
}
pendingAdditiveOperator.clear();
} else {
sumSoFar = operand;
}

display->setText(QString::number(sumSoFar));
sumSoFar = 0.0;
waitingForOperand = true;
}

与additiveOperatorClicked()一样,我们首先处理任何挂起的乘法和加法操作符,然后显示sumSoFar并将变量重置为零。必须将变量重置为零,以避免对值进行两次计数。

void Calculator::pointClicked()
{
if (waitingForOperand)
display->setText("0");
if (!display->text().contains('.'))
display->setText(display->text() + tr("."));
waitingForOperand = false;
}

pointClicked()槽向显示的内容添加一个小数点。

void Calculator::changeSignClicked()
{
QString text = display->text();
double value = text.toDouble();

if (value > 0.0) {
text.prepend(tr("-"));
} else if (value < 0.0) {
text.remove(0, 1);
}
display->setText(text);
}

changeSignClicked()槽改变显示值的符号,如果当前值为正,则在前面加一个负号;如果当前值为负,则从值中删除第一个字符(负号)。

void Calculator::backspaceClicked()
{
if (waitingForOperand)
return;

QString text = display->text();
text.chop(1);
if (text.isEmpty()) {
text = "0";
waitingForOperand = true;
}
display->setText(text);
}

backspaceclick()将删除显示中最右边的字符,如果得到一个空字符串,则显示“0”并将waitingForOperand设置为true。

void Calculator::clear()
{
if (waitingForOperand)
return;

display->setText("0");
waitingForOperand = true;
}

clear()槽将当前操作数重置为零,这相当于按退格键多次擦除整个操作数。

void Calculator::clearAll()
{
sumSoFar = 0.0;
factorSoFar = 0.0;
pendingAdditiveOperator.clear();
pendingMultiplicativeOperator.clear();
display->setText("0");
waitingForOperand = true;
}

clearAll()槽将计算器重置为初始状态。

void Calculator::clearMemory()
{
sumInMemory = 0.0;
}

void Calculator::readMemory()
{
display->setText(QString::number(sumInMemory));
waitingForOperand = true;
}

void Calculator::setMemory()
{
equalClicked();
sumInMemory = display->text().toDouble();
}

void Calculator::addToMemory()
{
equalClicked();
sumInMemory += display->text().toDouble();
}

clearMemory()插槽擦除保存在内存中的总和,readMemory()将总和显示为操作数,setMemory()将内存中的总和替换为当前的总和,addtommemory()将当前值添加到内存中的值。对于setMemory()和addtommemory(),我们首先调用equalClicked()来更新sumSoFar和显示中的值。

template<typename PointerToMemberFunction>
Button *Calculator::createButton(const QString &text, const PointerToMemberFunction &member)
{
Button *button = new Button(text);
connect(button, &Button::clicked, this, member);
return button;
}

私有的createButton()函数从构造函数中被调用来创建计算器按钮。

void Calculator::abortOperation()
{
clearAll();
display->setText(tr("####"));
}

私有的abortOperation()函数在计算失败时被调用,重置计算器状态,显示“####”。

bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
{
if (pendingOperator == tr("+")) {
sumSoFar += rightOperand;
} else if (pendingOperator == tr("-")) {
sumSoFar -= rightOperand;
} else if (pendingOperator == tr("\303\227")) {
factorSoFar *= rightOperand;
} else if (pendingOperator == tr("\303\267")) {
if (rightOperand == 0.0)
return false;
factorSoFar /= rightOperand;
}
return true;
}

私有的calculate()函数执行一个二进制操作,右操作数由rightOperand给出。对于加法操作符,左操作数为sumSoFar;对于乘法运算符,左操作数是factorSoFar。如果发生除零,函数返回false。

Qt Widget组件推荐
  • QtitanRibbon - Ribbon UI组件:是一款遵循Microsoft Ribbon UI Paradigm for Qt技术的Ribbon UI组件,QtitanRibbon致力于为Windows、Linux和Mac OS X提供功能完整的Ribbon组件。
  • QtitanChart - Qt类图表组件:是一个C ++库,代表一组控件,这些控件使您可以快速地为应用程序提供漂亮而丰富的图表。
  • QtitanDataGrid - Qt网格组件:提供了一套完整的标准 QTableView 函数和传统组件无法实现的独特功能。使您能够将不同来源的各类数据加载到一个快速、灵活且功能强大的可编辑网格中,支持排序、分组、报告、创建带状列、拖放按钮和许多其他方便的功能。
  • QtitanDocking:允许您像 Visual Studio 一样为您的伟大应用程序配备可停靠面板和可停靠工具栏。黑色、白色、蓝色调色板完全支持 Visual Studio 2019 主题!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1275080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

visual Studio MFC 平台实现图像增强中Gray-level slicing,Bit-plane slicing,对比度拉伸三种方法

MFC 实现图像增强–分段式变换 本文使用visual Studio MFC 平台实现图像增强中的第三大类分段式变换中的三种方法&#xff0c;包括Gray-level slicing&#xff0c;Bit-plane slicing&#xff0c;对比度拉伸&#xff0e; 关于其他MFC单文档工程可参考 01-Visual Studio 使用MFC …

ES-深入理解倒排索引

倒排索引 idproductdesc1新版 小米 至尊-纪念版手机1小米 NFC 手机3NFC手机4小米 耳机5华为 耳机6扫地机器人7华为 Mata………………term_indexterm dictionaryposting list------------------------------------小米1……100W华为6,7,9NFC76,90耳机5352红米643,98机器人645,9…

【开源】基于JAVA语言的考研专业课程管理系统

项目编号&#xff1a; S 035 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S035&#xff0c;文末获取源码。} 项目编号&#xff1a;S035&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 考研高校模块2.3 高…

SSM项目实战-POJO设计

1、schedule_db.sql CREATE DATABASE schedule_db CHARACTER SET utf8 ;USE schedule_db;CREATE TABLE sys_schedule (sid int NOT NULL AUTO_INCREMENT COMMENT 日程id,uid int DEFAULT NULL COMMENT 用户id,title varchar(50) DEFAULT NULL COMMENT 标题,completed int DEFAU…

浅学指针(4)函数指针数组和qsort的使用

系列文章目录 文章目录 系列文章目录前言1.函数指针数组的⽤途作用&#xff1a;可以让代码更简洁&#xff0c;逻辑更清晰 2. 回调函数回调函数就是⼀个通过函数指针调⽤的函数 3 . qsort函数qsort函数可以排序所有数据类型解释如图&#xff1a;![在这里插入图片描述](https://i…

CAP概念和三种情况、Redis和分布式事务的权衡

借鉴&#xff1a;https://cloud.tencent.com/developer/article/1840206 https://www.cnblogs.com/huanghuanghui/p/9592016.html 一&#xff1a;CAP概念和三种情况 1.概念&#xff1a; C全称Consistency&#xff08;一致性&#xff09;&#xff1a;这个表示所有节点返回的数…

基于SpringBoot高校心理教育辅导设计与实现

摘 要 随着Internet技术的发展&#xff0c;心理教育辅导系统应运而生&#xff0c;心理教育辅导系统为用户提供了一个更为便利的心理测试咨询平台。所以&#xff0c;为了充分满足高校学生心理教育辅导的需求&#xff0c;特开发了本高校心理教育辅导系统。 本高校心理教育辅导系统…

喜报 | 通付盾WAAP解决方案入选国家工业信息安全发展研究中心“2023年数字化转型自主创新解决方案优选案例”

为提升自主创新产品质量和技术创新能力&#xff0c;助力重点行业自主可控基础设施建设&#xff0c;加速重点行业数字化转型工作进程&#xff0c;促进重点行业产业链数字化升级&#xff0c;推动重点行业数字化、网络化、智能化发展。国家工业信息安全发展研究中心联合中国交通建…

CentOS系统环境搭建(二十二)——Git进阶

centos系统环境搭建专栏&#x1f517;点击跳转 文章目录 Git进阶1.更新代码2.只拉取Git仓库的部分目录内容2.1初始化空白仓库2.2配置git仓库2.3开启稀疏检出属性2.4设置指定拉取的目录2.5 更新代码 Git进阶 1.更新代码 基于上一篇文章CentOS系统环境搭建&#xff08;二十一&…

[Android]AlertDialog对话框

1.介绍AlertDialog AlertDialog是Android中常用的对话框组件之一&#xff0c;用于在应用程序中显示一些提示信息、警告、确认信息或者提供用户进行选择的界面。AlertDialog通常用于与用户进行交互&#xff0c;例如要求用户确认某个操作、输入一些信息或者从一组选项中选择一个。…

从文字到使用,一文读懂Kafka服务

文章目录 1. 什么是Kafka&#xff1f; 2. Kafka有什么特点&#xff0c;优缺点是什么&#xff1f; 3. Kafka 与 Zookeeper 服务的关系&#xff1f; 4. Kafka 的常见命令有哪些&#xff1f;5. Kafka 和 Zookeeper 服务如何结合&#xff1f;6. Kafka 生产者和消费者的流程图&#…

四川枢震栩电商:抖店的商品标题怎么设置?

在抖店平台上&#xff0c;商品标题是吸引顾客点击和购买的重要因素之一。一个好的商品标题能够吸引顾客的注意&#xff0c;准确传达商品的特点和卖点。那么&#xff0c;如何设置抖店的商品标题呢&#xff1f; 一、抖店的商品标题怎么设置&#xff1f; 首先&#xff0c;简洁明了…

单片机实验(三)

前言 实验一&#xff1a;利用定时器T1的中断控制P1.7引脚输出音频信号&#xff0c;启动蜂鸣器发出一段熟悉的与众不同的具有10个音节的音乐音频。 实验二&#xff1a;使用定时器/计数器来实现一个LCD显示年、月、日、星期 、时、分、秒的电子表&#xff0c;要求时和分可以方便…

云服务器anaconda(py39)+pytorch1.12.0(cu113)

用xshell连接ip地址&#xff0c;端口号22&#xff0c;输入用户密码 查看当前版本 conda -V conda info --envs 如果不是需要的版本&#xff0c;使用 anaconda-clean --yes rm -rf anaconda3 删除文件夹 安装anaconda 2022 10 py3.9 wget https://repo.anaconda.com/archi…

kafka 集群 ZooKeeper 模式搭建

Apache Kafka是一个开源分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用程序 Kafka 官网&#xff1a;Apache Kafka 关于ZooKeeper的弃用 根据 Kafka官网信息&#xff0c;随着Apache Kafka 3.5版本的发布&#xff0c;Zookeeper现…

看这里!精确的用户画像是如何一步步构建的?

1. 用户画像的定义 用户画像是指根据用户的个人信息、行为特征和偏好等数据来描绘和分析用户的一种方法。它是通过收集和分析用户的各种数据&#xff0c;以便更好地了解用户需求和行为模式&#xff0c;从而为企业提供个性化、精准化的产品和服务。 2. 构建用户画像的步骤 &…

If和else的紧缩版本(Python)

epsilon_max 3 epsilon 2 epsilon_increment 1 epsilon epsilon * (1 epsilon_increment) if epsilon < epsilon_max else epsilon_max print(epsilon)

读书笔记-《数据结构与算法》-摘要1[数据结构]

文章目录 [数据结构]1. String - 字符串2. Linked List - 链表2.1 链表的基本操作2.1.1 反转链表单向链表双向链表 2.1.2 删除链表中的某个节点2.1.3 链表指针的鲁棒性2.1.4 快慢指针 3. Binary Tree - 二叉树3.1 树的遍历3.2 Binary Search Tree - 二叉查找树 4. Queue - 队列…

训练 CNN 对 CIFAR-10 数据中的图像进行分类-keras实现

1. 加载 CIFAR-10 数据库 import keras from keras.datasets import cifar10# 加载预先处理的训练数据和测试数据 (x_train, y_train), (x_test, y_test) cifar10.load_data() 2. 可视化前 24 个训练图像 import numpy as np import matplotlib.pyplot as plt %matplotlib …

桶装水订水送水小程序具备以下主要功能

桶装水订水送水小程序具备以下主要功能&#xff1a; 对比传统的电话订水&#xff0c;订水小程序展现出显著的优势&#xff1a; 1. 便捷性&#xff1a;用户通过小程序就能轻松预订水桶&#xff0c;无需亲自出门&#xff0c;极大提升了生活的便捷度。 2. 即时性&#xff1a;送水…