用于UISystem的工具集

简介:上篇文章用于管理Unity中UGUI的工具系统UISystem-CSDN博客讲了UISystem,为了更加方便使用,我给他写了一个编辑器工具,下面展示代码和使用说明,具体详情不难看一下就看懂了。

一、代码部分

using QFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
public class 快速创建UI物体及绑定脚本 : EditorWindow
{
    private string objectName;
    private string UIScriptPathName = "Assets/Scripts";
    private string UIInScenePathName="Canvas";
    private string UIPrefabPathName= "Assets/Resources/UiPanel";
    private GameObject newObject;
    private Type scriptType;
    private string scriptFullName;
    private bool isWaitingForNextFrame = false;
    // 添加一个布尔变量来存储勾选框的状态
    private bool isChecked;
    // 添加一个布尔变量来存储basepanel的状态
    private bool isBasePanel;

    // 控制显示UI元素的变量
    private bool showPrefabOptions = false;
    private bool[] SelectObjectType = new bool[4];  // 3个选项,Image, Button, Text
    [MenuItem("MyEditor/UIKit")]
    public static void OpenWindow()
    {
        var window = (快速创建UI物体及绑定脚本)GetWindow(typeof(快速创建UI物体及绑定脚本), true);
        window.position = new Rect(500, 300, 600, 400);
        window.Show();
    }

    public void OnGUI()
    {
        #region UI名称
        // 开始一个水平布局
        GUILayout.BeginHorizontal();
        // 添加提示文字
        GUILayout.Label("输入需要创建UI名称:", GUILayout.Width(150));
        // 添加输入框,允许用户输入物体名称
        objectName = GUILayout.TextField(objectName, GUILayout.Width(300));
        // 结束水平布局
        GUILayout.EndHorizontal();
        #endregion
        #region UI脚本路径
        // 开始一个水平布局
        GUILayout.BeginHorizontal();
        // 添加提示文字
        GUILayout.Label("输入创建UI脚本路径:", GUILayout.Width(150));
        // 添加输入框,允许用户输入物体名称
        UIScriptPathName = GUILayout.TextField(UIScriptPathName, GUILayout.Width(300));
        // 结束水平布局
        GUILayout.EndHorizontal();
        #endregion
        #region UI场景中位置
        // 开始一个水平布局
        GUILayout.BeginHorizontal();
        // 添加提示文字
        GUILayout.Label("输入创建UI在场景中的位置:", GUILayout.Width(150));
        // 添加输入框,允许用户输入物体名称
        UIInScenePathName = GUILayout.TextField(UIInScenePathName, GUILayout.Width(300));
        // 结束水平布局
        GUILayout.EndHorizontal();
        #endregion
        #region 是否生成预制体勾选框
        // 在界面上方添加一个勾选框
        // 使用 Toggle 获取勾选框的状态,并根据状态执行不同的方法
        isChecked = EditorGUILayout.Toggle("是否生成预制体", isChecked); // 将勾选框的状态赋值给isChecked
        if (isChecked)  // 判断勾选框的状态
        {
            #region UI预制体生成路径
            GUILayout.BeginHorizontal();
            GUILayout.Label("输入UI预制体生成路径:", GUILayout.Width(150));
            UIPrefabPathName = GUILayout.TextField(UIPrefabPathName, GUILayout.Width(300));
            GUILayout.EndHorizontal();
            #endregion
            //生成预制体
        }
        #endregion
        #region 创建物体类型
        // 使用 GUIContent 来处理文字换行,避免文字超出
        GUILayout.BeginHorizontal();
        GUIContent labelContent = new GUIContent("选择创建的UI类型(默认为GameObject)");
        GUILayout.Label(labelContent, GUILayout.Width(220)); // 设置一个合适的宽度

        // 控制显示是否创建UI类型的 Toggle
        showPrefabOptions = EditorGUILayout.Toggle("", showPrefabOptions); // 可以设置为一个适合的宽度
        GUILayout.EndHorizontal(); // 结束水平布局
        if (showPrefabOptions)
        {
            // 使用Toggle模拟单选,确保只能勾选一个
            for (int i = 0; i < SelectObjectType.Length; i++)
            {
                bool isSelected = GUILayout.Toggle(SelectObjectType[i], GetObjectTypeLabel(i));
                if (isSelected && !SelectObjectType[i])
                {
                    // 如果当前项被选中,则取消其他项的选中状态
                    for (int j = 0; j < SelectObjectType.Length; j++)
                    {
                        SelectObjectType[j] = (i == j);  // 确保只有当前项被选中
                    }
                }
            }
        }
        #endregion
        #region 是否继承BasePanel
        GUILayout.BeginHorizontal();
        GUIContent labelContent2 = new GUIContent("是否继承BasePanel(总Panel必须继承)");
        GUILayout.Label(labelContent2, GUILayout.Width(220)); // 设置一个合适的宽度
        isBasePanel = EditorGUILayout.Toggle("", isBasePanel); // 将勾选框的状态赋值给isBasePanel
        GUILayout.EndHorizontal(); // 结束水平布局
        #endregion
        #region 创建按钮
        if (GUILayout.Button("Create UI Object"))
        {
            CreateUIObject();
        }
        #endregion
        
        if (isWaitingForNextFrame)
        {
            OnNextFrame();
            isWaitingForNextFrame = false;
        }
    }
    private string GetObjectTypeLabel(int index)
    {
        switch (index)
        {
            case 0: return "Image";
            case 1: return "Button";
            case 2: return "Text";
            case 3: return "总Panel";
            default: return "";
        }
    }
    private void CreateUIObject()
    {
        // 创建一个空物体
        newObject = new GameObject(objectName);
        // 找到父物体,如果UIPrefabPathName指向的是场景中已有物体的名字
        GameObject parentObject = GameObject.Find(UIInScenePathName);
        // 如果找到了父物体,则将新物体设置为父物体的子物体
        if (parentObject != null)
        {
            newObject.transform.SetParent(parentObject.transform);
        }
        else
        {
            // 如果没有找到父物体,可以选择将它添加到场景根目录,或者根据需求处理
            Debug.LogError("指定的父物体不存在: " + UIInScenePathName);
        }
        // 获取物体的名字
        objectName = newObject.name;

        if (showPrefabOptions)//确保勾选了要创建ui类型的物体在遍历
        {
            for (int i = 0; i < SelectObjectType.Length; i++)
            {
                if (SelectObjectType[i])//不是物体而是ui类型   && (i != SelectObjectType.Length - 1)
                {
                    RectTransform rectTransform = newObject.AddComponent<RectTransform>();
                    switch (i)
                    {
                        case 0: CreateUI_ImageTypeSet(); break;
                        case 1: CreateUI_ButtonTypeSet(); break;
                        case 2: CreateUI_TextTypeSet(); break;
                        case 3: CreateUI_BasePanelTypeSet(); break;
                    }
                    //这里可以添加每个类型默认的设置,比如字体,文本,颜色等
                }
            }
        }
       

        // 生成脚本的路径
        string scriptPath = UIScriptPathName +"/"+ objectName + ".cs";  //$"PathName/{objectName}.cs";

        Debug.Log(scriptPath);
        // 检查脚本是否已经存在
        if (!File.Exists(scriptPath))
        {
            // 创建一个新的脚本
            string scriptTemplate = CreateNewScript();

            // 写入脚本内容
            File.WriteAllText(scriptPath, scriptTemplate);
            // 刷新文件夹
            AssetDatabase.Refresh();
            // 监听脚本编译完成事件
            CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished;
        }
        else
        {
            Debug.LogError("脚本已经存在: " + scriptPath);
        }

        
    }
    // 监听编译完成事件
    private void OnAssemblyCompilationFinished(string assemblyPath, CompilerMessage[] compilerMessages)
    {
        Debug.Log("脚本编译完成了");
        // 检查编译的是否是目标脚本
        if (assemblyPath.Contains("Assembly-CSharp"))
        {
            scriptFullName = objectName + ", Assembly-CSharp";  // 确保使用完整类型名称
            GameObject newObject = GameObject.Find(UIInScenePathName); // 获取创建的物体
            scriptType = null;
            Debug.Log("下一帧");
            OnNextFrame();
            // 取消注册监听
            CompilationPipeline.assemblyCompilationFinished -= OnAssemblyCompilationFinished;
        }
        else
        {
            Debug.LogError("脚本编译没有完成");
        }
    }
    private void OnNextFrame()
    {
        Debug.Log("进入下一帧");
        scriptType = Type.GetType(scriptFullName);
        if (scriptType == null)
        {
            Debug.Log("空");
            isWaitingForNextFrame = true;
            Repaint(); // 如果脚本没加载,再次请求重绘
        }
        else
        {
            Debug.Log(scriptType);
            // 脚本加载完成,添加组件
            newObject.AddComponent(scriptType);
            if (isChecked)
            {
                // 检查UIPrefabPathName是否为空,避免路径为空的情况
                if (string.IsNullOrEmpty(UIPrefabPathName))
                {
                    Debug.LogError("预制体保存路径不能为空!");
                    return;
                }
                // 创建预制体
                string prefabPath = Path.Combine(UIPrefabPathName, objectName + ".prefab");

                // 使用PrefabUtility将物体保存为预制体
                PrefabUtility.SaveAsPrefabAsset(newObject, prefabPath);

            }
        }
    }

    private void CreateUI_ImageTypeSet()
    {
        newObject.AddComponent<UnityEngine.UI.Image>();
        (newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
    }
    private void CreateUI_ButtonTypeSet()
    {
        UnityEngine.UI.Image image = newObject.AddComponent<UnityEngine.UI.Image>();
        newObject.AddComponent<UnityEngine.UI.Button>();
        (newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
        (newObject.transform as RectTransform).sizeDelta = new Vector2(160,30);
    }
    private void CreateUI_TextTypeSet()
    {
        UnityEngine.UI.Text text = newObject.AddComponent<UnityEngine.UI.Text>();
        text.color = UnityEngine.Color.black;
        text.text = "New Text";
        (newObject.transform as RectTransform).anchoredPosition = Vector3.zero;
        (newObject.transform as RectTransform).sizeDelta = new Vector2(160, 30);
    }
    private string CreateNewScript()
    {
        string scriptNewTemplate = "";
        if (isBasePanel)
        {
            scriptNewTemplate = "using UnityEngine;\n\n" +
                "namespace QFramework\n" + "{\n" +
                "public class " + objectName+ " : BasePanel\n" + "{\n" + CreateNewScript_Supplement() + "}\n" + "}";//
        }
        else
        {
            scriptNewTemplate = "using UnityEngine;\n\n" +
                
                "public class " + objectName+" : MonoBehaviour\n"+ "{\n"  + "}";
        }
        return scriptNewTemplate;
    }
    private string CreateNewScript_Supplement()
    {
        string ChildForClose = "\"" + objectName + "面板关闭" + "\"";
        string ChildForHide = "\"" + objectName + "面板隐藏" + "\"";
        string ChildForOpen = "\"" + objectName + "面板打开" + "\"";
        string ChildForPause = "\"" + objectName + "面板暂停" + "\"";
        string ChildForResume = "\"" + objectName + "面板恢复" + "\"";
        string ChildForShowAgain = "\"" + objectName + "面板再次显示" + "\"";
        string overrideFunc = "";
        overrideFunc += "public override void ChildForOpen()\n" + "{\n" + "Debug.Log(" + ChildForOpen + ")" + ";\n" + "}\n";
        overrideFunc += "public override void ChildForResume()\n" + "{\n" + "Debug.Log(" + ChildForResume + ")" + ";\n" + "}\n";
        overrideFunc += "public override void ChildForShowAgain()\n" + "{\n" + "Debug.Log(" + ChildForShowAgain + ")" + ";\n" + "}\n";
        overrideFunc += "public override void ChildForPause()\n" + "{\n" + "Debug.Log(" + ChildForPause + ")" + ";\n" + "}\n";
        overrideFunc += "public override void ChildForHide()\n" + "{\n" + "Debug.Log(" + ChildForHide + ")" + ";\n" + "}\n";
        overrideFunc += "public override void ChildForClose()\n" + "{\n" + "Debug.Log(" + ChildForClose + ")" + ";\n" + "}\n";
        return overrideFunc;
    }
    private void CreateUI_BasePanelTypeSet()
    {
       
    }

}


有很多不完整的部分,不过不影响使用,比如使用 window.titleContent = new GUIContent("UISystem组装外界碎片UI层级及组件");更改编辑器工具名字而不是类名,再比如有废弃掉的方法string没有删除而是置空等等。

二、使用说明

编辑器说明:
1.输入需要创建UI名称:这里不能为空,为空则退出编辑器创建
2.输入创建UI脚本路径:这里默认目录为Assets/Scripts/UI,可以自己更改,为空则退出编辑器创建,插件可以自动创建目录文件夹
3.输入创建UI在场景中的位置:这里默认目录为Canvas,相对于场景根目录,建议Canvas下一级目录为BasePanel,负责模块化Panel,需要创建BasePanel预制体,为空或场景中为找见该目录则退出编辑器创建
4.是否生成预制体:如果勾选则输入预制体保存路径,否则不需要做改动。如果勾选后路径为空则退出编辑器创建
5.选择创建的UI类型(默认为GameObject):如果不勾选则默认为继承monobehaviour的空类,勾选则显示需要创建的Panel类型,建议Canvas下一级目录选择总Panel,并且继承BasePanel(如果需要使用UISystem)
6.是否继承BasePanel(总Panel必须继承):勾选后继承类为BasePanel,不过BasePanel继承了monobehaviour
7.继承的父物体名称:如需使用则为空或Canvas下一级目录,在一级目录下创建二级、三级等更多目录则自动更新一级目录,建议在一级目录后创建更深层目录填写,并且保证填写名称与输入创建UI在场景中的位置的内容在第一个/与第二个/之间的内容相同,会自动从第二个/前目录在场景中查找并更新预制体与相对路径。对于更多层次的目录,则继承填写的名称,如果按照建议则继承他的根父物体类,父物体继承BasePanel。

操作指南:
一、创建一级BasePanel目录
操作过程:
1.输入需要创建UI名称:SystemPanel
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas
4.是否生成预制体:√
输入UI预制体生成路径:Assets/Resources/UiPanel
5.选择创建的UI类型(默认为GameObject):总Panel
6.是否继承BasePanel(总Panel必须继承):√
7.继承的父物体名称:空,这里不需要继承
结果:
得到Canvas下一级目录SystemPanel并且有一个脚本为SystemPanel赋上,在Assets/Resources/UiPanel路径下有SystemPanel预制体,在Assets/Scripts有SystemPanel脚本
二、创建二级目录
操作过程:
1.输入需要创建UI名称:SystemPanelClickFunc
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas/SystemPanel
4.是否生成预制体:×
5.选择创建的UI类型(默认为GameObject):Button
6.是否继承BasePanel(总Panel必须继承):×
7.继承的父物体名称:SystemPanel
结果:
得到Canvas下一级目录SystemPanel下的二级目录SystemPanelClickFunc并且有一个脚本为SystemPanelClickFunc赋上,自动更新SystemPanel预制体,在Assets/Scripts有SystemPanelClickFunc脚本,SystemPanelClickFunc可以自己继承UISystem以及QFrameWork内的任何接口架构
三、创建三级目录
操作过程:
1.输入需要创建UI名称:SystemPanelThreeFunc
2.输入创建UI脚本路径:Assets/Scripts
3.输入创建UI在场景中的位置:Canvas/SystemPanel/SystemPanelClickFunc
4.是否生成预制体:×
5.选择创建的UI类型(默认为GameObject):Button
6.是否继承BasePanel(总Panel必须继承):×
7.继承的父物体名称:SystemPanel
结果:
得到Canvas下一级目录SystemPanel下的二级目录SystemPanelClickFunc下的三级目录SystemPanelThreeFunc并且有一个脚本为SystemPanelThreeFunc赋上,自动更新SystemPanel预制体,在Assets/Scripts有SystemPanelThreeFunc脚本,SystemPanelThreeFunc可以自己继承UISystem以及QFrameWork内的任何接口架构
以下以此类推

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

onlyoffice连接器 二次开发 合同等制式模板化技术开发方案【三】

一、期望效果 目前曹瑞版本onlyoffice已经实现&#xff1a;书签模式 和 控件模式&#xff0c;用以支持该方案。 【图1】字段绑定 【图2】模板发起 【图3】接入表单 思路讲解&#xff1a; 业务系统开发中通常希望能够通过绑定form字段给word&#xff0c;从而达到双向同步效果&am…

WPF+MVVM案例实战与特效(四十五)- 打造优雅交互:ListBox 的高级定制与行为触发(侧边菜单交互面板)

文章目录 1、引言2、案例效果3、案例实现1、依赖安装2、文件创建3、代码实现1、依赖引用与上下文2、个性化视觉效果:自定义 ItemContainerStyle3、页面样式与布局完整代码4、ViewModel 逻辑实现5、子界面代码:3、实现效果4、源代码获取5、总结1、引言 在WPF应用程序开发中,…

【优选算法】复写零

链接&#xff1a;1089. 复写零 - 力扣&#xff08;LeetCode&#xff09; 算法原理&#xff1a; 解法&#xff1a;双指针算法 根据“异地”操作&#xff0c;然后优化成双指针下的“就地”操作 1.先找到最后一个“复写”的数 1.先判断 cur 位置的值 2.决定 dest 向后移动一步或…

moviepy将图片序列制作成视频并加载字幕 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

ubuntu20.04安装imwheel实现鼠标滚轮调速

ubuntu20.04安装imwheel实现鼠标滚轮调速 Ubuntu 系统自带的设置中仅具备调节鼠标速度的功能&#xff0c;而无调节鼠标滚轮速度的功能。其默认的鼠标滚轮速度较为缓慢&#xff0c;在查看文档时影响尚可接受&#xff0c;但在快速浏览网页时&#xff0c;滚轮速度过慢会给用户带来…

ubuntu开机进入initramfs状态

虚拟机卡死成功起后进入了initramfs状态&#xff0c;可能是跟文件系统有问题或者检索不到根文件系统&#xff0c;或者是配置错误&#xff0c;系统磁盘等硬件问题导致 开机后进入如下图的界面&#xff0c; 文中有一条提示 要手动fsck 命令修复 /dev/sda1 命令如下 fsck /de…

STL格式转换为OBJ格式

STL格式与OBJ格式简介 STL格式 STL&#xff08;Stereo Lithography&#xff09;文件是一种用于3D打印和计算机辅助制造&#xff08;CAM&#xff09;的文件格式。它最初由3D Systems公司开发&#xff0c;主要用于立体光刻技术。STL文件通常分为二进制和ASCII两种格式&#xff…

git命令恢复/还原某个文件、删除远程仓库中的文件

有时刚创建的远程仓库&#xff0c;可能无意中把一些没用的文件上传到仓库&#xff0c;本文介绍一下怎么删除这些文件。 一、git命令恢复某个文件 第一步&#xff1a;拉取最新代码 git pull 第二步&#xff1a; 查看git 修改的文件状态 git status 第三步&#xff1a;查看…

Chapter 3-1. Detecting Congestion in Fibre Channel Fabrics

Chapter 3. Detecting Congestion in Fibre Channel Fabrics This chapter covers the following topics: 本章包括以下主题: Congestion detection workflow. Congestion detection metrics. Congestion detection metrics and commands on Cisco MDS switches. Automatic A…

音视频入门基础:MPEG2-TS专题(20)——ES流简介

《T-REC-H.222.0-202106-S!!PDF-E.pdf》第27页对ES进行了定义。ES流是PES packets&#xff08;PES包&#xff09;中编码的视频、编码的音频或其他编码的比特流。一个ES流&#xff08;elementary stream&#xff09;在具有且只有一个stream_id的PES packets序列中携带&#xff1…

python+opencv+棋盘格实现相机标定及相对位姿估计

pythonopencv棋盘格实现相机标定及相对位姿估计 引言1&#xff0c;使用相机采集含棋盘格图像14张2&#xff0c;进行相机标定&#xff08;1&#xff09;测试软件1标定结果&#xff08;内参及畸变系数&#xff09;&#xff08;2&#xff09;测试软件2标定结果&#xff08;内参及畸…

【笔记】学校教的SSH:远程连接到另一个电脑 并对其进行操作

前言&#xff1a;我开了两台虚拟机做这个实验 一台是主机A ubuntu 一台是主机B centos7 &#xff08;一&#xff09;这里是在ubuntu进行的操作 1.安装ssh sudo apt install ssh 2.确认ssh激活了 systemctl status ssh 然后如图 这里是在主机B操作 就是如此简单 远程连接…

(九)腾讯cloudstudio(ubuntu)+akiaaa大神 Stable Diffusion整合包 AI绘画教程

一、说明 在网上转了一圈&#xff0c;发现确实akiaaa大神的整合包不错&#xff0c;看看这界面就比我前面的流弊多了&#xff0c;后面我们就要把这个界面一步一步干出来 二、环境准备 这里和前面的一样 &#xff08;七&#xff09;腾讯cloudstudioStable-Diffusion-webui AI绘…

6UCPCI板卡设计方案:8-基于双TMS320C6678 + XC7K420T的6U CPCI Express高速数据处理平台

基于双TMS320C6678 XC7K420T的6U CPCI Express高速数据处理平台 1、板卡概述 板卡由我公司自主研发&#xff0c;基于6UCPCI架构&#xff0c;处理板包含双片TI DSP TMS320C6678芯片&#xff1b;一片Xilinx公司FPGA XC7K420T-1FFG1156 芯片&#xff1b;六个千兆网口&#xff…

【专题】2024年悦己生活消费洞察报告汇总PDF洞察(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p38654 在当今时代背景下&#xff0c;社会发展日新月异&#xff0c;人们的生活方式与消费观念正经历深刻变革。MoonFox 月狐数据的《2024 年悦己生活消费洞察报告》聚焦于这一充满活力与变化的消费领域。随着就业、婚姻等社会压力的…

路由器的原理

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网路安全入门系列 目录 路由器的原理一&#xff0c;路由器基础及相关…

Elasticsearch-分词器详解

什么是分词器 1、分词器介绍 对文本进行分析处理的一种手段&#xff0c;基本处理逻辑为按照预先制定的分词规则&#xff0c;把原始文档分割成若干更小粒度的词项&#xff0c;粒度大小取决于分词器规则。 常用的中文分词器有ik按照切词的粒度粗细又分为:ik_max_word和ik_smart&…

怿星科技联合赛力斯举办workshop活动,进一步推动双方合作

12月18日&#xff0c;由怿星科技与赛力斯汽车联合举办的workshop活动在赛力斯五云湖总部展开&#xff0c;双方嘉宾围绕智能汽车发展趋势、行业前沿技术、汽车电子网络与功能测试等核心议题展开了深度对话与交流&#xff0c;并现场参观演示了多套前沿产品。怿星科技CEO潘凯、汽车…

tomato靶场攻略

前提&#xff1a;kali和tomato的连接方式都为net模式 tomato的默认网络连接方式为桥接模式&#xff0c;导入前注意修改&#xff0c;将tomato.ova的镜像导入虚拟机中 出现此页面则表示导入成功&#xff0c;打开kali虚拟机终端&#xff0c;切换为root权限 arp-scan -l 浏览器访…

深度学习中,用损失的均值或者总和反向传播的区别

如深度学习中代码&#xff1a; def train_epoch_ch3(net, train_iter, loss, updater):"""The training loop defined in Chapter 3."""# Set the model to training modeif isinstance(net, torch.nn.Module):net.train()# Sum of training lo…