Chromium源码阅读:从页面加载到元素展示(1)

从<p>hello world</p>.html到界面上的hello world

今天,我们一起来看看一个html元素,是如何绘制到界面上。我们选择了最简单的场景,便于快速掌握总体的流程,加深之前阅读知识的印象。

准备环境

首先,我们保存这段html:

<html>
    <body>
        <p>Hello world</p>
    </body>
</html>

接下来,debug浏览器content_shell跑起来,加载这个文件:
在这里插入图片描述

开始分析

接下来,我们开始下断点分析流程。

根据我们这些天对chromium的了解,可以盲猜一下,总体的流程应该是:

加载命令->读取文件 -> 解析html -> 创建p元素 -> 创建渲染Node ->
压入绘制序列 ==> 到渲染进程 执行绘制序列 -> 执行合成(cc) -> 上屏幕(viz)
复习一下这个神图:
在这里插入图片描述

好的,那么我们一个一个断点验证我们的猜想,把关键堆栈截图出来,看到相关的模块和关键代码吧!

读取文件

很容易找打相关类FileURLLoader,打上断点,确认是我们的文件,这个类放在content/browser/loader下面:
在这里插入图片描述
堆栈只能看到FileURLLoader这一层,说明代码存在异步,只能通过搜索,找到异步的地方,再下个断点:
在这里插入图片描述
这次断点看到了FileURLLoaderFactory这个类,但是由于跨了进程(mojo)于是跟随线索,找到URLLoaderFactoryStubDispatch这个类,打上断点,找到了调用方:
在这里插入图片描述
这个堆栈在Browser进程,虽然堆栈有点深,但可以简而言之(顺序从1到3):

  1. content.dll!content::NavigationRequest::BeginNavigation() [content\browser\renderer_host\navigation_request.cc]
  2. content.dll!content::NavigationURLLoaderImpl::MaybeStartLoader()
    [content\browser\loader\navigation_url_loader_impl.cc]
  3. network_cpp.dll!network::mojom::URLLoaderFactoryProxy::CreateLoaderAndStart()
    [out\x64_debug\gen\services\network\public\mojom\url_loader_factory.mojom.cc]

目前,我们主要在Browser进程和NetWork进程转悠,还没到Render进程。

解析html

读取到文件之后,需要Parser解析,我们也很容易找到Praser,根据目录,找最大的源文件,并打上断点:
在这里插入图片描述
在这里插入图片描述
这段堆栈简而言之(顺序从1到6执行):

  1. content.dll!content::RenderFrameImpl::CommitNavigation [content\renderer\render_frame_impl.cc]
  2. blink_core.dll!blink::WebLocalFrameImpl::CommitNavigation [third_party\blink\renderer\core\frame\web_local_frame_impl.cc]
  3. blink_core.dll!blink::FrameLoader::CommitDocumentLoader[third_party\blink\renderer\core\loader\frame_loader.cc]
  4. blink_core.dll!blink::DocumentLoader::CommitNavigation() [third_party\blink\renderer\core\loader\document_loader.cc]
  5. blink_core.dll!blink::Document::CreateParser() [third_party\blink\renderer\core\dom\document.cc]
  6. blink_core.dll!blink::HTMLTreeBuilder::HTMLTreeBuilder() [third_party\blink\renderer\core\html\parser\html_tree_builder.cc]

构造HTMLTree

既然到了HTMLTreeBuilder,那我们可以去Element元素类打个断点,看看构造HTMLItem:
第一次进来,是个开始的伪元素:
在这里插入图片描述
跳过几个元素,到了我们的P元素:
在这里插入图片描述
构造元素的过程也不复杂,可以简化为:

  1. blink_core.dll!blink::HTMLDocumentParser::ConstructTreeFromToken(blink::AtomicHTMLToken &) [G:\chromium\src\third_party\blink\renderer\core\html\parser\html_document_parser.cc]
  2. blink_core.dll!blink::HTMLTreeBuilder::ConstructTree(blink::AtomicHTMLToken *) [G:\chromium\src\third_party\blink\renderer\core\html\parser\html_tree_builder.cc]
  3. blink_core.dll!blink::CustomElement::CreateUncustomizedOrUndefinedElement(blink::Document &) [G:\chromium\src\third_party\blink\renderer\core\html\custom\custom_element.cc]
  4. blink_core.dll!blink::HTMLElementFactory::Create(const WTF::AtomicString &) [G:\chromium\src\out\x64_debug\gen\third_party\blink\renderer\core\html_element_factory.cc]
  5. blink_core.dll!blink::HTMLParagraphElement::HTMLParagraphElement(blink::Document &) [G:\chromium\src\third_party\blink\renderer\core\html\html_paragraph_element.cc]

HTMLParagraphElement 就是我们的

元素。
HTMLParagraphElement 类不可谓不简单:
在这里插入图片描述
HTMLParagraphElement 这个类直接出现的地方也很少:
在这里插入图片描述

另外,有趣的是,HTMLElementFactory构造HTMLElement的时候,简单粗暴,就是查表:
在这里插入图片描述
另外,在看HtmlElement源码时发现了一个FastGetAttribute的函数,以为有什么黑魔法可以Fast,跟进去发现竟然是个遍历,看来此Fast非彼Fast。
在这里插入图片描述

接下来,该布局了Layout

背景知识:

1.块级元素
1.1、特征
霸占一行,不能与其他任何元素并列。
宽高设置有效,那么宽度将默认变为父级的百分百。
1.2、常用元素中那些属于块级元素
<h1>~<h6>,<p>,<div>,<ul>,<ol>,<li>等,所有的容器级标签,都是块级元素
2.行内元素
2.1、特征
设置宽高无效,能与其他行内元素并排,宽高由内容撑开。
水平方向上的padding和margin可以设置,垂直方向上的无效。
不会自动进行换行
2.2、常用元素中那些属于行内元素
如:p , span , a , b , i , u , em 等,所有的文本级标签,都是行内元素 ps: p元素比较特殊,在P元素里面只能放文字和图片和表单元素,p里面不能放h和ul,也不能放p。所有P元素属于行内元素,也属于行内块元素
3.行内块元素
3.1、特征
不自动换行,能够识别宽高
默认排列方式为从左到右
3.2、常用元素中那些属于行内块元素
img,input
4.三种元素互相转换
使用display属性可以转换标签的元素级别,其属性有三种设置方式如下:
block 可以把行内元素转换成块级元素
inline 把块级元素准换成行内元素
inline-block 转换成行内块元素

我们这里的p元素,在 HTML 中,

元素(段落)是一个块级元素(Block-level Element)。
于是,我们去找对应的LayoutNode:
在这里插入图片描述

在BlockNode下断点:
在这里插入图片描述
布局部分的主要流程如下:

  1. cc.dll!cc::ProxyMain::BeginMainFrame(s)[cc\trees\proxy_main.cc]
  2. cc.dll!cc::LayerTreeHost::RequestMainFrameUpdate(bool) [cc\trees\layer_tree_host.cc]
  3. blink_platform.dll!blink::LayerTreeView::UpdateLayerTreeHost()
    [third_party\blink\renderer\platform\widget\compositing\layer_tree_view.cc]
  4. blink_core.dll!blink::LocalFrameView::UpdateLayout() [third_party\blink\renderer\core\frame\local_frame_view.cc]
  5. blink_core.dll!blink::LayoutView::LayoutRoot() [third_party\blink\renderer\core\layout\layout_view.cc]
  6. blink_core.dll!blink::BlockNode::Layout(const blink::ConstraintSpace &)
    [third_party\blink\renderer\core\layout\block_node.cc]
  7. blink_core.dll!blink::BlockLayoutAlgorithm::Layout() [third_party\blink\renderer\core\layout\block_layout_algorithm.cc]

布局后,会拿到一个LayoutObject组成的树,其根是LayoutView:

LayoutView 类在类似 Blink(Chromium 项目的一部分)这样的 Web渲染引擎中是一个基础概念,:

  1. 布局树的根LayoutView 是布局树的根对象。在 Web 文档中,布局树表示所有视觉元素的空间关系和尺寸。文档中需要布局的每个元素都会有一个关联的 LayoutObject,而
    LayoutView 是最顶层的 LayoutObject

  2. 初始包含块(ICB):在 CSS 中,初始包含块是包含整个文档布局的矩形。它是布局中所有盒子的最终祖先,其尺寸等同于视口大小。LayoutView
    表示这个概念,并作为相对视口定位的元素(例如,固定或绝对定位的元素)的包含块。

  3. 与布局视口尺寸匹配LayoutView 的尺寸与布局视口的尺寸相匹配,布局视口是浏览器用来计算页面上元素大小的区域。布局视口对于响应式设计非常重要,用于确定不同屏幕尺寸上内容的布局方式。

  4. 相对于文档的位置为 (0,0)LayoutView 相对于文档的位置是坐标 (0,0)。这意味着它总是位于文档的左上角,但如果页面滚动了,它不一定在视口中可见。

  5. 树的共享成员:由于每个根布局树(或浏览器中的每个 Frame)有一个 LayoutView,这个类包括跨布局树共享的成员。例如,m_layoutState 可能是一个保存布局进程当前状态的成员,而
    m_layoutQuoteHead 可能与布局中引用的管理相关。

  6. 从 LayoutNGBlockFlow 继承LayoutView 类继承自 LayoutNGBlockFlow,这表明它是 Blink 的 LayoutNG(下一代布局)架构的一部分。LayoutNG
    旨在提高布局性能和精度,与传统的布局系统相比有所改进。作为一个块流(block flow),它还表明 LayoutView
    参与块格式化上下文布局操作(如定位块级元素和处理内容流)。

随着代码的阅读,笔者发现LocalFrameView、Document等实例都是LocalFrameView的类成员。可见,LocalFrameView代表了一个页面可视化的顶层逻辑。为此,笔者进一步了解了其他页面的顶层逻辑类的情况:

在 Blink 项目中,WebFrameWidgetLocalFrameLocalFrameView 是 Web渲染流程中的关键组件:

  1. LocalFrame:

    • LocalFrame 是 Blink 中的一个类,代表了一个框架(Frame),在 HTML 中通常对应一个 <iframe> 元素或顶级窗口。每个框架都有自己的文档和 DOM 树。
    • LocalFrame 负责处理输入事件、执行 JavaScript、处理导航请求、以及管理布局和绘制流程等。
    • 它是 Web 渲染的核心部分,负责处理和维护 Web 页面的内容和状态。
  2. LocalFrameView:

    • LocalFrameView 是与 LocalFrame 相关联的视图,代表了框架内容的可视化表现。它是一个布局树的根,负责计算和存储框架中所有元素的布局信息。
    • LocalFrameView 处理布局更新、绘制操作,以及视口的滚动和缩放等。
    • 它也是处理重绘和重流(reflow)的关键组件,即在 DOM 更新或样式变化时更新元素的布局和视觉表现。
  3. WebFrameWidget:

    • WebFrameWidget 是 Blink 对外提供的接口,它是一个与特定 LocalFrame 相关联的小部件(widget),通常是顶级框架或者有视觉表现的子框架(如有自己的层叠上下文的 <iframe>)。
    • 它负责框架的 UI 表现,如绘制、处理输入事件、以及与视图大小和缩放有关的操作。
    • WebFrameWidget 主要用于顶层框架和非顶层但需要独立输入处理的框架,是连接渲染引擎和浏览器 UI 之间的桥梁。

简而言之,LocalFrame 管理框架的逻辑和状态,LocalFrameView 管理框架的布局和视觉表现,而WebFrameWidget 是框架与外部交互的界面,负责处理输入和绘制。这三者共同工作,以确保 Web页面能够正确渲染并与用户交互。在实际的页面渲染过程中,它们各自承担不同的职责,但又相互依赖,共同构成了 Blink 渲染引擎的核心部分。

更进一步,笔者梳理出了Blink顶层逻辑的类,并放到一起综合列出:

在 Blink 渲染引擎中,各个类的功能和作用可以按照它们在渲染流程中的职责进行分类。以下是对这些类的分类和详细说明:

页面结构和框架管理

  1. Page

    • 代表整个浏览器窗口中的一个完整页面。
    • 协调属于同一页面的所有框架(LocalFrameRemoteFrame)。
    • 管理与页面生命周期相关的功能,视觉更新,全局事件处理等。
  2. FrameTree

    • 描述框架层级结构,管理页面中所有框架的树状结构。
    • 允许遍历和管理框架之间的父子关系。

框架表示

  1. LocalFrame

    • 代表一个框架,可以是顶级窗口或嵌套的 <iframe>
    • 负责处理输入事件、执行 JavaScript、处理导航请求、管理布局和绘制流程。
  2. RemoteFrame

    • 代表一个远程框架,内容在另一个进程中渲染。当使用跨域 iframe 或者分布式多进程架构(如 Chrome 的多进程架构)时,RemoteFrame 代表了一个远程框架。与 LocalFrame 相比,RemoteFrame 中的内容实际上是在另一个进程中渲染的。RemoteFrame 与 LocalFrame 并行,提供了一种机制来同步跨进程的框架状态和事件。
    • 主要用于同步跨进程框架LocalFrame的状态和事件。

文档和 DOM

  1. Document
    • 代表 LocalFrame 中的文档对象模型(DOM)。
    • 包含页面内容和结构,提供访问和修改 DOM 树的 API。

视图和渲染

  1. LocalFrameView

    • LocalFrame 相关联的视图,代表框架内容的可视化表现。
    • 处理布局更新、绘制操作,以及视口的滚动和缩放。
  2. RenderView

    • 渲染进程中的组件,协调一个 Page 的渲染过程。
    • WebView 对象相关联,负责显示整个页面的内容。

用户界面和交互

  1. WebFrameWidget

    • 与特定 LocalFrame 相关联的小部件(widget),通常用于顶级框架或有视觉表现的子框架。
    • 负责框架的 UI 表现,包括绘制、处理输入事件、以及与视图大小和缩放有关的操作。
  2. WebView

    • 浏览器 UI 层使用的接口,用于在浏览器中显示和操作整个页面的内容。
    • RenderView 协作,提供页面级别的渲染和交互。

以上类别组件通过协作,共同完成了从文档加载、DOM树构建、布局计算到最终的页面渲染和交互的整个过程。每个类都在渲染和显示 Web 页面的过程中扮演着特定的角色。

构造RenderTree

根据我们之前的阅读笔记,ElementTree构建出来之后,如果想要渲染出来,那么需要构造RenderTree。刚刚提到RenderView,那相关逻辑也肯定在那里。我们先看看RenderView是怎么创建出来的:

创建RenderView

在这里插入图片描述
跨进程了,再到另一个进程跟踪流程:
在这里插入图片描述

这两张截图的流程粗略为:

在Browser进程:

  1. content_shell.exe!content::Shell::LoadURL(const GURL &) [v8\samples\shell.cc]
  2. content.dll!content::FrameTreeNode::TakeNavigationRequest() [content\browser\renderer_host\frame_tree_node.cc]
  3. content.dll!content::RenderFrameHostManager::CreateSpeculativeRenderFrameHost()
    [content\browser\renderer_host\render_frame_host_manager.cc]
  4. content.dll!content::RenderFrameHostManager::CreateRenderFrameProxy()
    [content\browser\renderer_host\render_frame_host_manager.cc]
  5. content.dll!content::RenderViewHostImpl::CreateRenderView() [content\browser\renderer_host\render_view_host_impl.cc]
  6. content.dll!content::AgentSchedulingGroupHost::CreateView() [content\browser\renderer_host\agent_scheduling_group_host.cc]

在Render进程:
(通过mojo::AssociatedRemotemojom::AgentSchedulingGroup mojo_remote_到Render进程:)

  1. content.dll!content::AgentSchedulingGroup::CreateWebView() [content\renderer\agent_scheduling_group.cc]
  2. blink_core.dll!blink::WebView::Create(bool) [third_party\blink\renderer\core\exported\web_view_impl.cc]

在阅读RenderView的相关代码时,会遇到很多相似的类名的类,这里也一并梳理一下:

  1. RenderView

    • 如前所述,RenderView 是负责协调页面渲染的组件。它存在于渲染进程中,并与 WebView 相关联,负责管理一个 Page 的布局和绘制。
  2. RenderViewImpl

    • RenderViewImplRenderView 的具体实现类。它具体实现了渲染视图的功能,如处理页面的布局、绘制和事件等。
  3. RenderViewHost

    • RenderViewHost 存在于浏览器进程中,是渲染进程中 RenderView 的代理(或对应物)。它负责与渲染进程中的 RenderView 进行通信,发送命令和接收事件。
  4. RenderViewHostImpl

    • RenderViewHostImplRenderViewHost 的具体实现。它实现了与渲染视图交互所需的逻辑,并管理着渲染进程和浏览器进程之间的通信。
  5. RenderViewHostFactory

    • RenderViewHostFactory 是一个工厂类,用于创建 RenderViewHost 的实例。它允许在测试或其他特殊场景中替换或自定义 RenderViewHost 的创建过程。
  6. RenderViewDelegate

    • RenderViewDelegate 是一个接口,定义了浏览器进程期望从 RenderView 接收的事件和命令。RenderView 的实现类(如 RenderViewImpl)将通过这个接口与浏览器进程进行交互。
  7. RenderViewImplScaleFactor

    • 这个类是特定于缩放功能的实现,负责处理与 RenderViewImpl 相关的缩放因子调整。

理解这些类的最佳方式是将它们分为两个主要类别:渲染进程中的实现类(如 RenderViewImpl)和浏览器进程中的管理类(如
RenderViewHostRenderViewHostImpl)。渲染进程中的类直接负责页面的渲染工作,而浏览器进程中的类则负责管理、协调和与渲染进程通信。

在实际的代码架构中,这些类之间的交互可能会涉及到复杂的多进程通信,但它们的基本职责还是围绕着页面的渲染和浏览器进程与渲染进程之间的通信。因此,当尝试理解这些类时,关注其所属的进程(渲染进程还是浏览器进程)以及它们在渲染和通信流程中的角色会有所帮助。

《未完待续 … 》

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

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

相关文章

深入理解并打败C语言难关之一————指针(4)

前言&#xff1a; 我们在前面的几讲中已经讲了指针的很多内容了&#xff0c;现在我们开始层层递进&#xff0c;要探寻更多的指针喽&#xff0c;不多废话了&#xff0c;直接进入正题&#xff0c;开始今天的指针之旅喽&#xff01; 目录&#xff1a; 1.字符指针变量 1.1常量字符…

除了程序员,你又是谁呢?别说!保护自己能量最好的方式——早读(逆天打工人爬取热门微信文章解读)

你很困的时候&#xff0c;会不会遵循本心直接睡觉呢&#xff1f; 引言Python 代码第一篇 洞见 保护自己能量最好的方式第二篇 视频新闻结尾 引言 现在真的是越来越遵循本心了 昨天晚上10点多 觉得好困 但是又没有洗澡 然后就想着算了 躺一个 没想到一躺 早上6点了 起来速速洗刷…

2024年心理学研究、现代化教育与社会发展国际学术会议(PRMESD 2024)

2024年心理学研究、现代化教育与社会发展国际学术会议(PRMESD 2024) 2024 International Conference on Psychological Research, Modern Education and Social Development 会议地点&#xff1a;南京&#xff0c;中国 网址&#xff1a;www.prmesd.com 邮箱: prmesdsub-con…

浔川计算机v1.1——浔川python科技社

浔川计算机v1.1 import tkinter import math import tkinter.messageboxclass Calculator(object):# 界面布局方法def __init__(self):# 创建主界面,并且保存到成员属性中self.root tkinter.Tk()self.root.minsize(280, 450)self.root.maxsize(280, 470)self.root.title(浔川计…

LabVIEW 32位与64位版本比较分析:性能与兼容性详解

LabVIEW的32位和64位版本在功能、性能、兼容性和应用场景等方面存在差异。本文从系统要求、内存管理、性能、兼容性、驱动支持和开发维护等多个角度进行详细分析&#xff0c;帮助用户选择合适的版本。 一、系统要求 操作系统支持&#xff1a; 32位LabVIEW&#xff1a;可以在32位…

vue+elementUI实现在表格中添加输入框并校验的功能

背景&#xff1a; vue2elmui 需求&#xff1a; 需要在一个table中添加若干个输入框&#xff0c;并且在提交时需要添加校验 思路&#xff1a; 当需要校验的时候可以考虑添加form表单来触发校验&#xff0c;因此需要在table外面套一层form表单&#xff0c;表单的属性就是ref…

ComfyUI 宝藏插件之辅助工具

今天我们就来分享下这个 ComfyUI 辅助脚本工具的功能。 插件安装&#xff0c;小伙伴们直接在管理器里搜索「ComfyUI-Custom-Scripts」&#xff0c;点击安装就可以了&#xff0c;这里再告诉小伙伴们一个小技巧&#xff0c;点击名称可以跳转到插件所在的官网哦。 没有安装管理器…

Tdengine的时序数据库简介、单机部署、操作语句及java应用

Tdengine的时序数据库简介、单机部署、操作语句及java应用 本文介绍了Tdengine的功能特点、应用场景、超级表和子表等概念&#xff0c;讲述了Tdengine2.6.0.34的单机部署&#xff0c;并介绍了taos数据库的常见使用方法及特色窗口查询方法&#xff0c;最后介绍了在java中的应用。…

AI助力密码安全:利用机器学习提升密码安全性

信息安全已经成为了当今数字世界的一个核心问题&#xff0c;随着互联网技术使用场景的不断增加&#xff0c;创建和管理安全的密码已经成为了保证在线账户安全的关键要求。本文将研究和探讨如何利用人工智能&#xff08;AI&#xff09;和机器学习技术来提升密码的安全性。 学习目…

xgo 原理探索

Go 单测 mock 方案 Mock 方法原理依赖优点缺点接口 Mock为依赖项定义接口&#xff0c;并提供接口的 Mock 实现。需要定义接口和 Mock 实现。灵活&#xff0c;遵循 Go 的类型系统&#xff1b;易于替换实现。需要更多的样板代码来定义接口和 Mock 实现。Monkey Patching&#xf…

深度学习网络结构之---Inception

目录 一、Inception名称的由来 二、Inception结构 三、Inception v2 四、Inception v3 1、深度网络的通用设计原则 2.卷积分解&#xff08;Factorizing Convolutions&#xff09; 3.对称卷积分解 3.非对称卷积分解 五、Inception v4 一、Inception名称的由来 Inception网…

推荐一款好用的读论文软件操作方法

步骤&#xff1a; 1. 使用一译 —— 文档和论文翻译、对照阅读、讨论和社区 2.上传自己想要翻译的论文即可。 示例 Planing论文双语翻译 1.1 Parting with Misconceptions about Learning-based Vehicle Motion Planning 中英文对照阅读 1.2 Rethinking Imitation-based Pl…

3.多层感知机

目录 1.感知机训练感知机XOR问题&#xff08;Minsky&Papert 1969&#xff09; AI的第一个寒冬总结 2.多层感知机(MLP)学习XOR单隐藏层&#xff08;全连接层&#xff09;激活函数&#xff1a;Sigmoid激活函数&#xff1a;Tanh激活函数&#xff1a;ReLu 最常用的 因为计算速度…

AMSR-MODIS 边界层水汽 L3 每日 1 度 x 1 度 V1、V2 版本数据集

AMSR-MODIS Boundary Layer Water Vapor L3 Daily 1 degree x 1 degree V1 (AMDBLWV) at GES DISC AMSR-MODIS Boundary Layer Water Vapor L3 Daily 1 degree x 1 degree V2 (AMDBLWV) at GES DISC 简介 该数据集可估算均匀云层下的海洋边界层水汽。AMSR-E 和 AMSR-2 的微波…

使用libcurl实现简单的HTTP访问

代码; #include <stdio.h> #include <stdlib.h> #include <curl/curl.h> // 包含libcurl库 FILE *fp; // 定义一个文件标识符 size_t write_data(void *ptr,size_t size,size_t nmemb,void *stream) { // 定义回调函数&#xff0c;用于将…

MGRS坐标

一 概述 MGRS坐标系统&#xff0c;即军事格网参考系统&#xff0c;是北约(NATO)军事组织使用的标准坐标系统。它基于UTM&#xff08;通用横向墨卡托&#xff09;系统&#xff0c;并将每个UTM区域进一步划分为100km100km的小方块。这些方块通过两个相连的字母标识&#xff0c;其…

华为云开发者社区活动-基于MindNLP的ChatGLM-6B聊天机器人体验

MindNLP ChatGLM-6B StreamChat 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。支持流式回复。 本活动通过配置环境&#xff0c;模型接入&#xff0c;以及gradio前端界面搭建&#xff0c;实现了聊天机器人的功能。 以下是一些体验记录&#xff1a; 有兴趣的可以通过以下链…

详细解析找不到msvcp120.dll文件的原因及解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到msvcp120.dll”。这个错误提示通常出现在运行某些程序或游戏时&#xff0c;给使用者带来了困扰。那么&#xff0c;究竟是什么原因导致了这个问题的出现&#xff1f;又该如何解决呢&a…

【每日刷题】Day64

【每日刷题】Day64 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. LCP 67. 装饰树 - 力扣&#xff08;LeetCode&#xff09; 3. 1315. 祖父节点值为偶数的节点和 - 力…

PyQT5 中关于 QCheckBox 的勾选状态的一点小细节

一、QCheckBox 是 PyQt5 中的一个用于创建复选框的控件&#xff0c;以下是其一些常见方法和属性&#xff1a; setChecked: 设置复选框的选中状态。isChecked: 检查复选框是否被选中。text: 设置或获取复选框的文本。state: 获取复选框的状态&#xff08;无、选中、不可用等&am…