当前位置: 首页 > article >正文

QT软件开发环境及简单图形的绘制-图形学(实验一)-[成信]

对于软件的安装这里就不多介绍了。

本文章主要是根据本校图形学的实验知道来做。

创建一个简单的计算机图形学程序

第一步:创建项目及配置

这里创建的项目名和类名尽量和我的一样,避免后面直接复制我的代码时会出现一些名字上面的错误。QtWidgetsApplication1

【注意:这里创建的路径名里面不能出现中文

其余选默认即可,到下面这里类名的创建尽量与我的一致。QtWidgetsApplication1

接下来的设置都选择默认即可。

遇到这里出现感叹号是因为还没有进行配置。

第二步:

接下来在Qt UI下填加菜单,菜单名为“app1, 生成下拉列表项test, 对应右侧的属性编辑器objectName属性值为actiontest, 更新并保存QtWidgetsApplication1.ui文件(UI文件,可编辑)

这里添加菜单,菜单名为“app1“。

生成下拉列表项test。

属性编辑器objectName属性值为actiontest,这里默认已经是了,不需要修改。

完成后修改Ctrl+shift+s保存文件。

第三步:代码编写。

实验要求及步骤如下:(先大致浏览一下即可)

在头文件QtWidgetsApplication1.h中添加槽函数

public slots:

void test();

在头文件QtWidgetsApplication1.h中添加QmessageBox头文件

#include<qmessagebox.h>

QtWidgetsApplication1.cpp的构造函数QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)

: QMainWindow(parent)中,ui.setupUi(this)函数之后添加:

connect(ui.actiontest, SIGNAL(triggered()), this, SLOT(test()));

QtWidgetsApplication1.cpp完成测试函数test()

void QtWidgetsApplication1::test()

{

    QMessageBox::information(NULL, "Title", "OK");

}

 回到“编辑”板块,先对Qtwidgetsapplication1.h文件进行修改,修改要求如上。

这里可能一些同学不清楚怎么添加,可以将我下面的代码直接替换该文件的所有内容就行了。

#ifndef QTWIDGETSAPPLICATION1_H
#define QTWIDGETSAPPLICATION1_H
#include<qmessagebox.h>
#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class QtWidgetsApplication1; }
QT_END_NAMESPACEclass QtWidgetsApplication1 : public QMainWindow
{Q_OBJECTpublic slots:void test();public:QtWidgetsApplication1(QWidget *parent = nullptr);~QtWidgetsApplication1();private:Ui::QtWidgetsApplication1 *ui;
};
#endif // QTWIDGETSAPPLICATION1_H

这里旁边的红线不是报错的意思,只是说明这里进行了修改没有保存的提示,所以我们

Ctrl+shift+s保存就没有该红线了。 

接下来是对Qtwidgetsapplication1.cpp问价的编写,修改要求如上叙述过了。

这里也是防止一些同学不清楚怎么添加,可以将我下面的代码直接替换该文件的所有内容就行了。

#include "Qtwidgetsapplication1.h"
#include "ui_Qtwidgetsapplication1.h"QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent): QMainWindow(parent), ui(new Ui::QtWidgetsApplication1)
{ui->setupUi(this);connect(ui->actiontest, SIGNAL(triggered()), this, SLOT(test()));
}QtWidgetsApplication1::~QtWidgetsApplication1()
{delete ui;
}void QtWidgetsApplication1::test()
{QMessageBox::information(NULL, "Title", "OK");
}

【注意对于这里自己动手添加的同学原要求里有一点小错,如下进行修改就行,这里可能是因为版本的问题】 

接下来就可以 运行查看结果了。

查看实验结果:

【如果上述操作都正确但不能正常运行或者出现以下的错误就是因为你没有好好得读该文章,这些错误是因为项目的创建路径里出现了中文,注意这点后重新操作上述步骤】

或者

简单图形的绘制

实验要求如下:

第一步:添加绘图事件

头文件QtWidgetsApplication1.h中添加

#include <QPainter>

添加void paintEvent(QPaintEvent*);

public:

    QtWidgetsApplication1(QWidget *parent = Q_NULLPTR);

void paintEvent(QPaintEvent*);

QtWidgetsApplication1.cpp文件中添加

void QtWidgetsApplication1::paintEvent(QPaintEvent*)

{

    QPainter painter(this);

    painter.setPen(QPen(Qt::blue, 4));

    painter.drawLine(20, 20, 220, 220);

    painter.drawLine(20, 220, 220, 20);

    painter.drawEllipse(20, 20, 200, 200);

    painter.drawRect(20, 20, 200, 200);

}

执行效果:

执行程序

这里主要就对那两个文件进行修改就行了。接下来我就简述的给出两个文件的代码就行,有兴趣的同学可以根据要求自己来添加代码:

对于Qtwidgetsapplication1.h,直接替换下述代码就行,并进行保存

#ifndef QTWIDGETSAPPLICATION1_H
#define QTWIDGETSAPPLICATION1_H#include <QMainWindow>
#include <QMessageBox>    // 已存在
#include <QPainter>       // 已存在
#include <QPaintEvent>    // 新增:paintEvent需要QPaintEvent类型QT_BEGIN_NAMESPACE
namespace Ui { class QtWidgetsApplication1; }
QT_END_NAMESPACEclass QtWidgetsApplication1 : public QMainWindow
{Q_OBJECTpublic:// 原构造函数已存在,避免重复声明QtWidgetsApplication1(QWidget *parent = nullptr); // 保持nullptr或改用Q_NULLPTR~QtWidgetsApplication1();public slots:void test();protected:// 正确的作用域:paintEvent应为protectedvoid paintEvent(QPaintEvent* event) override; // 添加override确保正确重写private:Ui::QtWidgetsApplication1 *ui;
};#endif // QTWIDGETSAPPLICATION1_H

对于Qtwidgetsapplication1.cpp,直接替换下述代码就行,并进行保存

#include "Qtwidgetsapplication1.h"
#include "ui_Qtwidgetsapplication1.h"QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent): QMainWindow(parent), ui(new Ui::QtWidgetsApplication1)
{ui->setupUi(this);connect(ui->actiontest, SIGNAL(triggered()), this, SLOT(test()));
}QtWidgetsApplication1::~QtWidgetsApplication1()
{delete ui;
}void QtWidgetsApplication1::test()
{QMessageBox::information(NULL, "Title", "OK");
}// 新增绘图事件实现
void QtWidgetsApplication1::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);  // 标记未使用参数避免警告QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);  // 抗锯齿// 绘制蓝色图形painter.setPen(QPen(Qt::blue, 4));painter.drawLine(20, 20, 220, 220);painter.drawLine(20, 220, 220, 20);painter.drawEllipse(20, 20, 200, 200);painter.drawRect(20, 20, 200, 200);
}

完成进行运行就能看到实验结果了,如下: 

 DDA方法直线的绘制:

实验要求如下:

在h文件中添加

public:

    QtWidgetsApplication1(QWidget *parent = Q_NULLPTR);

    void paintEvent(QPaintEvent*);

    void DDA(QImage* image, int x1, int y1, int x2, int y2, QRgb value);

private:

    Ui::QtWidgetsApplication1Class ui;

    QPainter* paint;

在cpp文件中添加

void QtWidgetsApplication1::paintEvent(QPaintEvent*)

{

    /*

    QPainter painter(this);

    painter.setPen(QPen(Qt::blue, 4));

    painter.drawLine(20, 20, 220, 220);

    painter.drawLine(20, 220, 220, 20);

    painter.drawEllipse(20, 20, 200, 200);

    painter.drawRect(20, 20, 200, 200);

    */

    QImage image(800, 600, QImage::Format_RGB32);

    paint = new QPainter;

    paint->begin(this);

    DDA(&image, 120, 120, 300, 150, qRgb(255, 0, 0));

    paint->drawImage(0, 0, image);

    paint->end();

}

void QtWidgetsApplication1::DDA(QImage* image, int x1, int y1, int x2, int y2, QRgb value)

{

    int x;

    float dx, dy, y, k;

    dx = x2 - x1;

    dy = y2 - y1;

    k = dy / dx;

    y = y1;

    for (x = x1; x < x2; x++)

    {

        image->setPixel(x, int(y + 0.5), value);

        y = y + k;

    }

}

实现效果如下图:

这里也是主要就对那两个文件进行修改就行了。接下来我就简述的给出两个文件的代码就行,有兴趣的同学可以根据要求自己来添加代码:

对于Qtwidgetsapplication1.h,直接替换下述代码就行,并进行保存

#ifndef QTWIDGETSAPPLICATION1_H
#define QTWIDGETSAPPLICATION1_H#include <QMainWindow>
#include <QMessageBox>
#include <QPainter>
#include <QPaintEvent>
#include <QImage>  // 新增:DDA函数需要QImage类型QT_BEGIN_NAMESPACE
namespace Ui {class QtWidgetsApplication1; // UI类名应与实际生成的一致
}
QT_END_NAMESPACEclass QtWidgetsApplication1 : public QMainWindow
{Q_OBJECTpublic:// 构造函数声明(保持唯一性)QtWidgetsApplication1(QWidget *parent = nullptr); // 使用nullptr(C++11标准)~QtWidgetsApplication1();public slots:void test();protected:// 绘图事件必须声明在protected区域void paintEvent(QPaintEvent* event) override;private:// DDA算法函数声明(根据需求选择public/private)void DDA(QImage* image, int x1, int y1, int x2, int y2, QRgb value);private:Ui::QtWidgetsApplication1 *ui; // 保持指针类型,与生成的UI类一致// 注意:不建议将QPainter作为成员变量(见下方解释)
};#endif // QTWIDGETSAPPLICATION1_H

对于Qtwidgetsapplication1.cpp,直接替换下述代码就行,并进行保存

#include "Qtwidgetsapplication1.h"
#include "ui_Qtwidgetsapplication1.h"QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent): QMainWindow(parent), ui(new Ui::QtWidgetsApplication1)
{ui->setupUi(this);connect(ui->actiontest, SIGNAL(triggered()), this, SLOT(test()));
}QtWidgetsApplication1::~QtWidgetsApplication1()
{delete ui;
}void QtWidgetsApplication1::test()
{QMessageBox::information(NULL, "Title", "OK");
}void QtWidgetsApplication1::paintEvent(QPaintEvent* event)
{Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 创建白色背景的QImageQImage image(800, 600, QImage::Format_RGB32);image.fill(Qt::white);// 使用DDA绘制红色线段DDA(&image, 120, 120, 300, 150, qRgb(255, 0, 0));// 绘制到窗口左上角painter.drawImage(0, 0, image);
}void QtWidgetsApplication1::DDA(QImage* image, int x1, int y1, int x2, int y2, QRgb value)
{int dx = x2 - x1;int dy = y2 - y1;int steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);if (steps == 0) {image->setPixel(x1, y1, value);return;}float xIncrement = static_cast<float>(dx) / steps;float yIncrement = static_cast<float>(dy) / steps;float x = x1;float y = y1;for (int i = 0; i <= steps; ++i) {// 检查坐标是否在图像范围内if (x >= 0 && x < image->width() && y >= 0 && y < image->height()) {image->setPixel(static_cast<int>(x + 0.5), static_cast<int>(y + 0.5), value);}x += xIncrement;y += yIncrement;}
}

完成进行运行就能看到实验结果了,如下: 

历经数日的代码打磨、效果调试和文档整理,这篇QT实验手记终于得以和大家见面。作为一名技术分享者,你们的每一次「👍点赞」都是深夜Debug时的暖心咖啡,每一句「💬观点碰撞」都能让我在技术路上走得更稳更远。

如果在实践中发现文中存在表述偏差,或对某个技术细节有更优雅的实现思路,欢迎在评论区挥毫指点——你的真知灼见不仅会帮助我完善内容,更能为后来者点亮一盏明灯。让我们用开源精神共同构建更有价值的开发者社区,下篇技术见!

 

相关文章:

QT软件开发环境及简单图形的绘制-图形学(实验一)-[成信]

对于软件的安装这里就不多介绍了。 本文章主要是根据本校图形学的实验知道来做。 创建一个简单的计算机图形学程序 第一步&#xff1a;创建项目及配置 这里创建的项目名和类名尽量和我的一样&#xff0c;避免后面直接复制我的代码时会出现一些名字上面的错误。QtWidgetsAppl…...

项目部署一次记录

链路&#xff1a;&#xff08;用户&#xff09;客户端 → Nginx:192.168.138.100→ Tomcat &#xff08;程序&#xff09;:192.168.138.101→ MySQL/Redis 打开数据库&#xff1a;systemctl start mysqld 重启网络&#xff1a; systemctl restart NetworkManager 关闭防火墙&am…...

单例模式,饿汉式,懒汉式,在java和spring中的体现

目录 饿汉式单例模式 懒汉式单例模式 Spring中的单例模式 关键差异对比 在Java和Spring中的应用场景 手写案例 单例模式是一种创建型设计模式&#xff0c;其核心在于确保一个类仅有一个实例&#xff0c;并提供一个全局访问点来获取该实例。下面将详细介绍饿汉式和懒汉式…...

一文带你彻底理清C 语言核心知识 与 面试高频考点:从栈溢出到指针 全面解析 附带笔者手写2.4k行代码加注释

引言&#xff1a;C 语言的魅力与挑战 从操作系统内核到嵌入式系统&#xff0c;从高性能计算到网络编程&#xff0c;C 语言高效、灵活和贴近硬件的特性&#xff0c;始终占据着不可替代的地位。然而&#xff0c;C 语言的强大也伴随着较高的学习曲线&#xff0c;尤其是指针、内存管…...

【Redis】第1节|Redis服务搭建

一、Redis 基础概念 核心功能 内存数据库&#xff0c;支持持久化&#xff08;RDB/AOF&#xff09;、主从复制、哨兵高可用、集群分片。常用场景&#xff1a;缓存、分布式锁、消息队列、计数器、排行榜等。 安装环境 依赖 GCC 环境&#xff08;C语言编译&#xff09;&#xff0…...

数据结构第5章 树与二叉树(竟成)

第 5 章 树与二叉树 【考纲内容】 1.树的基本概念 2.二叉树 &#xff08;1&#xff09;二叉树的定义及其主要特征 &#xff08;2&#xff09;二叉树的顺序存储结构和链式存储结构 &#xff08;3&#xff09;二叉树的遍历 &#xff08;4&#xff09;线索二叉树的基本概念和构造 …...

# 深入解析BERT自然语言处理框架:原理、结构与应用

深入解析BERT自然语言处理框架&#xff1a;原理、结构与应用 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;框架的出现无疑是一个重要的里程碑。它凭借其强大的语言表示能…...

ai学习--python部分-1.变量名及命名空间的存储

初学代码时总有一个问题困扰我&#xff1a;a 10 # a指向地址0x1234&#xff08;存储10&#xff09; 变量a的值10存储在0x1234&#xff0c;那么变量a需要存储吗&#xff1f;a又存储在什么地址呢 目录 1. ​​命名空间的本质​​ 2. ​​命名空间的内存占用​​ 3. ​​…...

Cadence学习笔记之---PCB过孔替换、封装更新,DRC检查和状态查看

目录 01 | 引 言 02 | 环境描述 03 | 过孔替换 04 | 封装更新 05 | PCB状态查看 06 | DRC检查 07 | 总 结 01 | 引 言 终于终于来到了Cadence学习笔记的尾声&#xff01; 在上一篇文章中&#xff0c;讲述了如何布线、如何铺铜&#xff0c;以及布线、铺铜过程中比较重要…...

Java基础 Day21

一、Stream 流 思想&#xff1a;流水线式思想 1、获取流对象&#xff08;将数据放到流中&#xff09; &#xff08;1&#xff09;集合获取 Stream 流对象 使用Collection接口的默认方法 default Stream<E> stream() 获取当前集合对象的 Stream 流&#xff08;单列集…...

系统开发和运行知识

软件生存周期 软件生存周期包括可行性分析与项目开发计划、需求分析、概要设计、详细设计、编码和单元测试、综合测试及维护阶段。 1、可行性分析与项目开发计划 主要任务是确定软件的开发目标及可行性。该阶段应该给出问题定义、可行性分析和项目开发计划。 2、需求分析 需求…...

Elasticsearch 分片驱逐(Shard Exclusion)方式简析:`_name`、`_ip`、`_host`

在日常运维 Elasticsearch 集群过程中&#xff0c;常常需要将某个节点上的分片迁移出去&#xff0c;例如下线节点、腾出资源或进行维护操作。Elasticsearch 提供了简单直观的 shard exclusion 参数来实现这一目的&#xff0c;主要通过以下三个配置项&#xff1a; cluster.rout…...

【C++高级主题】异常处理(四):auto_ptr类

目录 一、auto_ptr 的诞生&#xff1a;为异常安全的内存分配而设计 1.1 传统内存管理的痛点 1.2 auto_ptr 的核心思想&#xff1a;RAII 与内存绑定 1.3 auto_ptr 的基本定义&#xff08;简化版&#xff09; 二、auto_ptr 的基本用法&#xff1a;将指针绑定到智能对象 2.1…...

STM32CubeMX配置使用通用定时器产生PWM

一、定时器PWM功能简介 定时器&#xff0c;顾名思义&#xff0c;就是定时的功能&#xff0c;定时器在单片机中算是除GPIO外最基本的外设。在ST中&#xff0c;定时器分为几种&#xff0c;基础定时器&#xff0c;通用定时器&#xff0c;高级定时器和低功耗定时器。其中定时器除了…...

WebSphere Application Server(WAS)8.5.5教程第十四讲:JPA

一、JPA 以下是对 JPA&#xff08;Java Persistence API&#xff09; 的深入详解&#xff0c;适用于具备一定 Java EE / Jakarta EE 背景的开发者&#xff0c;尤其是对数据持久化机制感兴趣的人员。 1、什么是 JPA&#xff1f; Java Persistence API&#xff08;JPA&#xf…...

Linux系统调用深度剖析

Linux系统调用深度剖析与实践案例 目录 Linux系统调用深度剖析与实践案例 一、Linux系统调用概述 二、进程管理相关系统调用 1. fork():进程克隆与多任务处理 2. exec系列:程序加载与替换 3. wait/waitpid:进程状态同步 三、文件操作相关系统调用 1. 文件描述符操作…...

动态规划-918.环形子数组的最大和-力扣(LeetCode)

一、题目解析 听着有点复杂&#xff0c;这里一图流。 将环形问题转化为线性问题。 二、算法原理 1.状态表示 2.状态转移方程 详细可以移步另一篇博客&#xff0c;53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 3.初始化 由于计算中需要用到f[i-1]和g[i-1]的值&…...

Docker 镜像标签(Tag)规范与要求

Docker 镜像标签(Tag)规范与要求 背景 目前主流云厂商&#xff0c;如阿里云、百度云和腾讯云&#xff0c;均提供租户使用的镜像仓库服务。 各个厂商要求可能不太一样&#xff0c;比如华为&#xff1a;https://doc.hcs.huawei.com/zh-cn/usermanual/swr/swr_faq_0017.html 样…...

STM32:Modbus通信协议核心解析:关键通信技术

知识点1【 Modbus通信】 1、Modbus的概述 Modbus是OSI模型第七层的应用层报文传输协议 协议&#xff1a;说明有组包和解包的过程 2、通信机制 Modelbus是一个请求/应答协议 通信机制&#xff1a;主机轮询&#xff0c;从机应答的机制。每个从设备有唯一的地址&#xff0c;主…...

线程封装与互斥

目录 线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 互斥量的接口 初始化互斥量有两种方法&#xff1a; 销毁互斥量 互斥量加锁和解锁 改进售票系统 互斥量实现原理探究 互斥量的封装 线程互斥 进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共…...

鸿蒙OSUniApp 开发实时天气查询应用 —— 鸿蒙生态下的跨端实践#三方框架 #Uniapp

使用 UniApp 开发实时天气查询应用 —— 鸿蒙生态下的跨端实践 在移动互联网时代&#xff0c;天气应用几乎是每个人手机中的"标配"。无论是出行、旅游还是日常生活&#xff0c;实时获取天气信息都极为重要。本文将以"实时天气查询应用"为例&#xff0c;详…...

第十一天 5G切片技术在车联网中的应用

前言 在自动驾驶汽车每天产生4TB数据的时代&#xff0c;传统的移动网络已难以满足车联网的海量连接需求。中国移动2023年实测数据显示&#xff0c;某智能网联汽车示范区在传统5G网络下&#xff0c;紧急制动指令的传输延迟高达65ms&#xff0c;而5G网络切片技术将这个数值降低到…...

Spring AI 系列之一个很棒的 Spring AI 功能——Advisors

1. 概述 由AI驱动的应用程序已成为我们的现实。我们正在广泛地实现各种RAG应用程序、提示API&#xff0c;并利用大型语言模型&#xff08;LLM&#xff09;创建项目。借助 Spring AI&#xff0c;我们可以更快速地完成这些任务。 在本文中&#xff0c;我们将介绍一个非常有价值…...

Vue3 + TypeScript + el-input 实现人民币金额的输入和显示

输入人民币金额的参数要求&#xff1a; 输入要求&#xff1a; 通过键盘&#xff0c;只允许输入负号、小数点、数字、退格键、删除键、方向左键、方向右键、Home键、End键、Tab键&#xff1b;负号只能在开头&#xff1b;只保留第一个小数点&#xff1b;替换全角输入的小数点&a…...

2.1 C++之条件语句

学习目标&#xff1a; 理解程序的分支逻辑&#xff08;根据不同条件执行不同代码&#xff09;。掌握 if-else 和 switch 语句的用法。能编写简单的条件判断程序&#xff08;如成绩评级、游戏选项等&#xff09;。 1 条件语句的基本概念 什么是条件语句&#xff1f; 程序在执…...

ZYNQ实战:可编程差分晶振Si570的配置与动态频率切换

为什么需要可编程差分晶振? 在现代FPGA和嵌入式系统中,高速串行通信(如GTP/GTX收发器)对参考时钟的精度和灵活性要求极高。例如,1G以太网需要125MHz时钟,SATA协议需120MHz,而DisplayPort则需135MHz。传统固定频率晶振无法满足多协议动态切换需求,而Si570凭借其10MHz~8…...

Linux `ls` 命令深度解析与高阶应用指南

Linux `ls` 命令深度解析与高阶应用指南 一、核心功能解析1. 基本作用2. 与类似命令对比二、选项系统详解1. 常用基础选项2. 进阶筛选选项三、高阶应用技巧1. 组合过滤查询2. 格式化输出控制3. 元数据深度分析四、企业级应用场景1. 存储空间监控2. 安全审计3. 自动化运维五、特…...

【MPC控制 - 从ACC到自动驾驶】5. 融会贯通:MPC在ACC中的优势总结与知识体系构建

【MPC控制 - 从ACC到自动驾驶】融会贯通&#xff1a;MPC在ACC中的优势总结与知识体系构建 在过去的四天里&#xff0c;我们一起经历了一段奇妙的旅程&#xff1a; Day 1: 我们认识了自适应巡航ACC这位“智能领航员”&#xff0c;并初见了模型预测控制MPC这位“深谋远虑的棋手…...

Day3 记忆内容:map set 高频操作

以下是 第三天 的详细学习内容&#xff0c;聚焦 map和set的高效应用&#xff0c;重点突破查找类题型和去重逻辑&#xff0c;助你提升代码效率&#xff01; &#x1f4da; Day3 记忆内容&#xff1a;map & set 高频操作 1. map 核心操作&#xff08;手写3遍&#xff09; /…...

初等数论--Garner‘s 算法

0. 介绍 主要通过混合积的表示来逐步求得同余方程的解。 对于同余方程 { x ≡ v 0 ( m o d m 0 ) x ≡ v 1 ( m o d m 1 ) ⋯ x ≡ v k − 1 ( m o d m k − 1 ) \begin{equation*} \begin{cases} x \equiv v_0 \quad (\ \bmod \ m_0)\\ x \equiv v_1 \quad (\ \bmod \ m_1)…...