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

跟着cherno手搓游戏引擎【3】事件系统和预编译头文件

不多说了直接上代码,课程中的架构讲的比较宽泛,而且有些方法写完之后并未测试。所以先把代码写完。理解其原理,未来使用时候会再此完善此博客。

文件架构:

Event.h:核心基类

#pragma once
#include"../Core.h"
#include<string>
#include<functional>
namespace YOTO {// Hazel中的事件当前是阻塞的,这意味着当一个事件发生时,它立即被分派,必须立即处理。//将来,一个更好的策略可能是在事件总线中缓冲事件,并在更新阶段的“事件”部分处理它们。//事件类型enum class EventType{None = 0,WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,AppTick, AppUpdate, AppRender,KeyPressed, KeyReleased,MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled};//事件分类enum EventCategory {None = 0,EventCategoryApplication = BIT(0),EventCategoryInput = BIT(1),EventCategoryKeyboard = BIT(2),EventCategoryMouse = BIT(3),EventCategoryMouseButton = BIT(4)};//定义一些重复的重写的函数,只需要传入固定参数,就能少些一大部分代码
#define EVENT_CLASS_TYPE(type) static EventType GetStaticType(){return EventType::##type; }\virtual EventType GetEventType() const override {return GetStaticType();}\virtual const char* GetName() const override {return #type;}
#define EVENT_CLASS_CATEGORY(category) virtual int GetCategoryFlags() const override {return category; }/// <summary>/// 事件基类,事件需要继承此类/// </summary>class Event{public://获得该事件的具体类型virtual EventType GetEventType() const = 0;//获得事件的名称virtual const char* GetName() const = 0;//获得该事件的大类型virtual int GetCategoryFlags() const = 0;//toString方法,返回事件名virtual std::string ToString() const { return GetName(); }//判断该事件是否是category类inline bool IsInCategory(EventCategory category) {return GetCategoryFlags() & category;}protected://事件是否被处理了bool m_Handled = false;};/// <summary>/// 基于事件类型调度事件的方法(暂时没有测试,不知道怎么用)/// </summary>class EventDispatcher{template <typename T>using EventFn = std::function<bool(T&)>;public:EventDispatcher(Event& event):m_Event(event) {}template <typename T>bool Dispatch(EventFn<T> func) {if (m_Event.GetEventType() == T::GetStaticType()) {m_Event.m_Handled = func(*(T*)&m_Event);return true;}return false;}private:Event& m_Event;};//暂无测试,方便输出inline std::ostream& operator<<(std::ostream& os, const Event& e){return  os << e.ToString();}}

 ApplicationEvent.h:处理appwindow的各种事件

#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API WindowResizeEvent :public Event {public:WindowResizeEvent(unsigned int width, unsigned int height) :m_Width(width), m_Height(height) {}inline unsigned int GetWidth() const { return m_Width; }inline unsigned int GetHeight() const { return m_Height; }std::string ToString() const override {std::stringstream ss;ss << "窗口大小改变事件:" << m_Width << "," << m_Height;return ss.str();}EVENT_CLASS_TYPE(WindowResize)EVENT_CLASS_CATEGORY(EventCategoryApplication)private:unsigned int m_Width, m_Height;};class YOTO_API WindowCloseEvent :public Event{public:WindowCloseEvent(){}EVENT_CLASS_TYPE(WindowClose)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppTickEvent :public Event {public:AppTickEvent(){}EVENT_CLASS_TYPE(AppTick)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppUpdateEvent :public Event {public:AppUpdateEvent(){}EVENT_CLASS_TYPE(AppUpdate)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppRenderEvent :public Event {public:AppRenderEvent() {}EVENT_CLASS_TYPE(AppRender)EVENT_CLASS_CATEGORY(EventCategoryApplication)};
}

KeyEvent.h:处理键盘事件

#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API KeyEvent:public Event{public:inline int GetKeyCode() const { return m_KeyCode; }EVENT_CLASS_CATEGORY(EventCategoryKeyboard | EventCategoryInput)protected:KeyEvent(int keycode):m_KeyCode(keycode){}int m_KeyCode;};class YOTO_API KeyPressedEvent :public KeyEvent{public:KeyPressedEvent(int keycode, int repeatCount):KeyEvent(keycode),m_RepeatCount(repeatCount){}inline int GetRepeatCount() const { return m_RepeatCount; }std::string ToString() const override{std::stringstream ss;ss << "键盘按下事件:" << m_KeyCode << "(" << m_RepeatCount << "重复)";return ss.str();}//static EventType GetStaticType() { return EventType::KeyPressed; }//virtual EventType GetEventType()const override { return GetStaticType(); }//virtual const char* GetName()const override { return "KeyPressed"; }EVENT_CLASS_TYPE(KeyPressed)private:int m_RepeatCount;};class KeyReleasedEvent:public KeyEvent{public:KeyReleasedEvent(int keycode):KeyEvent(keycode){}std::string ToString()const override {std::stringstream ss;ss << "键盘释放事件:" << m_KeyCode;return ss.str(); }EVENT_CLASS_TYPE(KeyReleased)};}

 MouseEvent.h:处理鼠标事件

#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API MouseMovedEvent :public Event{public:MouseMovedEvent(float x, float y):m_MouseX(x), m_MouseY(y) {}inline float GetX() const { return m_MouseX; }inline float GetY() const { return m_MouseY; }std::string ToString() const override {std::stringstream ss;ss << "鼠标移动事件:" << m_MouseX << "," << m_MouseY;return ss.str();}EVENT_CLASS_TYPE(MouseMoved)EVENT_CLASS_CATEGORY(EventCategoryMouse |EventCategoryInput )private:float m_MouseX, m_MouseY;};class YOTO_API MouseScrolledEvent :public Event{public:MouseScrolledEvent(float x, float y):m_XOffset(x), m_YOffset(y) {}inline float GetXOffset() const { return m_XOffset; }inline float GetYOffset() const { return m_YOffset; }std::string ToString() const override {std::stringstream ss;ss << "鼠标滚动事件:" << GetXOffset() << "," << GetYOffset();return ss.str();}EVENT_CLASS_TYPE(MouseScrolled)EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)private:float m_XOffset, m_YOffset;};class YOTO_API MouseButtonEvent :public Event{public:inline int GetMouseButton() const { return m_Button; }EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)protected:MouseButtonEvent(int button):m_Button(button){}int m_Button;};class YOTO_API MouseButtonPressedEvent :public MouseButtonEvent{public:MouseButtonPressedEvent(int button) :MouseButtonEvent(button) {}std::string ToString() const override {std::stringstream ss;ss << "鼠标按下事件:" << m_Button;return ss.str();}EVENT_CLASS_TYPE(MouseButtonPressed)};class YOTO_API MouseButtonReleasedEvent :public MouseButtonEvent{public:MouseButtonReleasedEvent(int button):MouseButtonEvent(button) {}std::string ToString() const override {std::stringstream ss;ss << "鼠标松开事件:" << m_Button;return ss.str();}EVENT_CLASS_TYPE(MouseButtonReleased)};
}

说白了就是写了个基类,然后写了实现类。

修改:

Core.h

#pragma once
//用于dll的宏
#ifdef YT_PLATFORM_WINDOWS
#ifdef YT_BUILD_DLL
#define YOTO_API __declspec(dllexport) 
#else
#define YOTO_API __declspec(dllimport) #endif // DEBUG
#else
#error YOTO_ONLY_SUPPORT_WINDOWS
#endif // YOTO_PLATFORM_WINDOWS
#define BIT(x)(1<<x)

记得加Event.h的头文件

premake5.lua

workspace "YOTOEngine"		-- sln文件名architecture "x64"	configurations{"Debug","Release","Dist"}
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"project "YOTOEngine"		--Hazel项目location "YOTOEngine"--在sln所属文件夹下的Hazel文件夹kind "SharedLib"--dll动态库language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录-- 包含的所有h和cpp文件files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 包含目录includedirs{"%{prj.name}/src","%{prj.name}/vendor/spdlog-1.x/include"}-- 如果是window系统filter "system:windows"cppdialect "C++17"-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错staticruntime "On"	systemversion "latest"	-- windowSDK版本-- 预处理器定义defines{"YT_PLATFORM_WINDOWS","YT_BUILD_DLL"}-- 编译好后移动Hazel.dll文件到Sandbox文件夹下postbuildcommands{("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")}-- 不同配置下的预定义不同filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"project "Sandbox"location "Sandbox"kind "ConsoleApp"language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}")objdir ("bin-int/" .. outputdir .. "/%{prj.name}")files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 同样包含spdlog头文件includedirs{"YOTOEngine/vendor/spdlog-1.x/include","YOTOEngine/src"}-- 引用hazellinks{"YOTOEngine"}filter "system:windows"cppdialect "C++17"staticruntime "On"systemversion "latest"defines{"YT_PLATFORM_WINDOWS"}filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"

测试: 

Application.cpp

#include "Application.h"
#include"Event/ApplicationEvent.h"
#include"Log.h"
namespace YOTO {Application::Application() {}Application::~Application() {}void Application::Run() {WindowResizeEvent e(1280, 720);if (e.IsInCategory(EventCategoryApplication)) {YT_CORE_TRACE(e);}if (e.IsInCategory(EventCategoryInput)) {YT_CORE_ERROR(e);}while (true){}}
}

 预编译头文件:

ytpch.h

#pragma once
#include<iostream>
#include<memory>
#include<utility>
#include<algorithm>
#include<functional>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<sstream>
#ifdef YT_PLATFORM_WINDOWS
#include<Windows.h>
#endif // YT_PLATFORM_WINDOWS

ytpch.cpp

#include "ytpch.h"

premake5.lua:

workspace "YOTOEngine"		-- sln文件名architecture "x64"	configurations{"Debug","Release","Dist"}
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"project "YOTOEngine"		--Hazel项目location "YOTOEngine"--在sln所属文件夹下的Hazel文件夹kind "SharedLib"--dll动态库language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录pchheader "ytpch.h"pchsource "YOTOEngine/src/ytpch.cpp"-- 包含的所有h和cpp文件files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 包含目录includedirs{"%{prj.name}/src","%{prj.name}/vendor/spdlog-1.x/include"}-- 如果是window系统filter "system:windows"cppdialect "C++17"-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错staticruntime "On"	systemversion "latest"	-- windowSDK版本-- 预处理器定义defines{"YT_PLATFORM_WINDOWS","YT_BUILD_DLL"}-- 编译好后移动Hazel.dll文件到Sandbox文件夹下postbuildcommands{("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")}-- 不同配置下的预定义不同filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"project "Sandbox"location "Sandbox"kind "ConsoleApp"language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}")objdir ("bin-int/" .. outputdir .. "/%{prj.name}")files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 同样包含spdlog头文件includedirs{"YOTOEngine/vendor/spdlog-1.x/include","YOTOEngine/src"}-- 引用hazellinks{"YOTOEngine"}filter "system:windows"cppdialect "C++17"staticruntime "On"systemversion "latest"defines{"YT_PLATFORM_WINDOWS"}filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"

把所有使用库文件的地方都换成#include"ytpch.h"即可

相关文章:

跟着cherno手搓游戏引擎【3】事件系统和预编译头文件

不多说了直接上代码&#xff0c;课程中的架构讲的比较宽泛&#xff0c;而且有些方法写完之后并未测试。所以先把代码写完。理解其原理&#xff0c;未来使用时候会再此完善此博客。 文件架构&#xff1a; Event.h:核心基类 #pragma once #include"../Core.h" #inclu…...

排序算法之快速排序

快速排序是一种高效的排序算法&#xff0c;它的基本思想是采用分治策略&#xff0c;将一个无序数组分割成两个子数组&#xff0c;分别对子数组进行排序&#xff0c;然后将两个排序好的子数组合并成一个有序数组。快速排序的性能优于归并排序&#xff0c;尤其在处理大规模数据时…...

Docker 从入门到实践:Docker介绍

前言 在当今的软件开发和部署领域&#xff0c;Docker已经成为了一个不可或缺的工具。Docker以其轻量级、可移植性和标准化等特点&#xff0c;使得应用程序的部署和管理变得前所未有的简单。无论您是一名开发者、系统管理员&#xff0c;还是IT架构师&#xff0c;理解并掌握Dock…...

用IDEA创建/同步到gitee(码云)远程仓库(保姆级详细)

前言&#xff1a; 笔者最近在学习java&#xff0c;最开始在用很笨的方法&#xff1a;先克隆远程仓库到本地&#xff0c;再把自己练习的代码从本地仓库上传到远程仓库&#xff0c;很是繁琐。后发现可以IDEA只需要做些操作可以直接把代码上传到远程仓库&#xff0c;也在网上搜了些…...

【Linux】进程控制深度了解

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握Linux下的进程控制 > 毒鸡汤&#xff…...

kbdnso.dll文件缺失,软件或游戏报错的快速修复方法

很多小伙伴遇到电脑报错&#xff0c;提示“kbdnso.dll文件缺失&#xff0c;程序无法启动执行”时&#xff0c;不知道应该怎样处理&#xff0c;还以为是程序出现了问题&#xff0c;想卸载重装。 首先&#xff0c;先要了解“kbdnso.dll文件”是什么&#xff1f; kbdnso.dll是Win…...

Spring技术内幕笔记之IOC的实现

IOC容器的实现 依赖反转&#xff1a; 依赖对象的获得被反转了&#xff0c;于是依赖反转更名为&#xff1a;依赖注入。许多应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的&#xff0c;这使得每个对象都需要与其合作的对象的引用&#xff0c;如果这个获取过程需要自身…...

kotlin foreach 循环

java中的foreach循环也使用于kotlin &#xff0c;先回顾下java里面的foreach循环 java foreach循环格式 for(元素类型t 元素变量x : 遍历对象obj){引用了x的语句;} 例如&#xff1a; int[] intary {1,2,3,4};for (int a: intary) {Log.d("intary", String.value…...

分享相关知识

直接使用海龟图进行创作移动动态的游戏 这段代码是一个简单的turtle模块实现的小游戏&#xff0c;主要功能包括&#xff1a; 窗口和无人机初始化&#xff1a; 创建了一个turtle窗口&#xff0c;设置了窗口的背景颜色和标题。创建了一个表示无人机的turtle&#xff0c;形状为正…...

RabbitMQ(七)ACK 消息确认机制

目录 一、简介1.1 背景1.2 定义1.3 如何查看确认/未确认的消息数&#xff1f; 二、消息确认机制的分类2.1 消息发送确认1&#xff09;ConfirmCallback方法2&#xff09;ReturnCallback方法3&#xff09;代码实现方式一&#xff1a;统一配置a.配置类a.生产者c.消费者d.测试结果 …...

ubuntu 编译内核报错

Ubuntu 编译 Linux 内核经常会遇到如下错误&#xff1a; 如果报错 canonical-certs.pem&#xff1a; 如下&#xff1a; make[1]: *** No rule to make target ‘debian/canonical-certs.pem’, needed by ‘certs/x509_certificate_list’. Stop. make: *** [Makefile:1868: …...

Python之自然语言处理库snowNLP

一、介绍 SnowNLP是一个python写的类库&#xff0c;可以方便的处理中文文本内容&#xff0c;是受到了TextBlob的启发而写的&#xff0c;由于现在大部分的自然语言处理库基本都是针对英文的&#xff0c;于是写了一个方便处理中文的类库&#xff0c;并且和TextBlob不同的是&…...

C# 语法进阶 委托

1.委托 委托是一个引用类型&#xff0c;其实他是一个类&#xff0c;保存方法的指针 &#xff08;指针&#xff1a;保存一个变量的地址&#xff09;他指向一个方法&#xff0c;当我们调用委托的时候这个方法就立即被执行 关键字&#xff1a;delegate 运行结果&#xff1a; 思…...

开源可观测性平台Signoz(四)【链路监控及数据库中间件监控篇】

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 前文链接&#xff1a; ​​开源可观测性平台Signoz系列&#xff08;一&#xff09;【开篇】​​ ​​开源可观测性平台Signoz&…...

【嵌入式开发 Linux 常用命令系列 4.2 -- git .gitignore 使用详细介绍】

文章目录 .gitignore 使用详细介绍.gitignore 文件的位置.gitignore 语法规则使用示例注意事项 .gitignore 使用详细介绍 .gitignore 文件是一个特殊的文本文件&#xff0c;它告诉 Git 哪些文件或目录是可以被忽略的&#xff0c;即不应该被纳入版本控制系统。这主要用于避免一…...

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…...

微服务雪崩问题及解决方案

雪崩问题 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个其它微服务。 微服务之间相互调用&#xff0c;因为调用链中的一个服务故障&#xff0c;引起整个链路都无法访问的情况。 如果服务提供者A发生了故障&#xff0c;当前的应用的部分业务…...

008、所有权

所有权可以说是Rust中最为独特的一个功能了。正是所有权概念和相关工具的引入&#xff0c;Rust才能够在没有垃圾回收机制的前提下保障内存安全。 因此&#xff0c;正确地了解所有权概念及其在Rust中的实现方式&#xff0c;对于所有Rust开发者来讲都是十分重要的。在本文中&…...

千里马2023年终总结-android framework实战

背景&#xff1a; hi粉丝朋友们&#xff1a; 2023年马上就过去了&#xff0c;很多学员朋友也都希望马哥这边写个年终总结&#xff0c;因为这几个月时间都忙于新课程halsystracesurfaceflinger专题的开发&#xff0c;差点都忘记了这个事情了&#xff0c;今天特别花时间来写个bl…...

vue3中pinia的使用及持久化(详细解释)

解释一下pinia&#xff1a; Pinia是一个基于Vue3的状态管理库&#xff0c;它提供了类似Vuex的功能&#xff0c;但是更加轻量化和简单易用。Pinia的核心思想是将所有状态存储在单个store中&#xff0c;并且将store的行为和数据暴露为可响应的API&#xff0c;从而实现数据&#…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...