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

【Unity编辑器拓展】GraphView自定义可视化节点

1、创建节点区域脚本

其中的new class UxmlFactory,可以让该元素显示在UI Builder中,我们就可以在Library-Project中看到我们新建的这两个UI元素,就可以拖入我们的UI窗口编辑了

public class NodeTreeViewer : GraphView
{public new class UxmlFactory : UxmlFactory<NodeTreeViewer, UxmlTraits> { }
}

默认的GraphView是一片黑屏。在这里,我们给我们的GraphView窗口添加上网格和拖拽缩放功能。

public class NodeTreeViewer : GraphView
{public new class UxmlFactory : UxmlFactory<NodeTreeViewer, UxmlTraits> { }public NodeTree tree;public Action<NodeView> OnNodeSelected;public NodeTreeViewer(){Insert(0, new GridBackground());this.AddManipulator(new ContentZoomer());this.AddManipulator(new ContentDragger());this.AddManipulator(new SelectionDragger());this.AddManipulator(new RectangleSelector());var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/NodeEditor/Editor/UI/NodeTreeViewer.uss");styleSheets.Add(styleSheet);}
}

     uss代码参考,上面代码的uss路径要根据项目实际路径进行设置。 

GridBackground{--grid-background-color: rgb(40,40,40);--line-color: rgba(193,196,192,0.1);--thick-line-color: rgba(193,196,192,0.1);--spacing: 15;
}

2、创建节点和删除选中元素

2.1 创建节点类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;public class NodeView : UnityEditor.Experimental.GraphView.Node
{public Node node;public Port input;public Port output;public Action<NodeView> OnNodeSelected;public NodeView(Node node){this.node = node;this.title = node.name;this.viewDataKey = node.guid;style.left = node.position.x;style.top = node.position.y;CreateInputPorts();CreateOutputPorts();}//创建输入端口private void CreateInputPorts(){input = InstantiatePort(Orientation.Vertical, Direction.Input, Port.Capacity.Multi, typeof(bool));if(input != null ){input.portName = "";inputContainer.Add(input);}}//创建输出端口private void CreateOutputPorts(){output = InstantiatePort(Orientation.Vertical, Direction.Output, Port.Capacity.Multi, typeof(bool));if (output != null){output.portName = "";outputContainer.Add(output);}}//选中该节点时传递事件public override void OnSelected(){base.OnSelected();if( OnNodeSelected != null ){OnNodeSelected?.Invoke(this);}}//设置生成时位置public override void SetPosition(Rect newPos){base.SetPosition(newPos);node.position.x = newPos.xMin;node.position.y = newPos.yMin;}}

2.2 节点区域创建节点和删除选中元素功能 

//重写该方法,可以添加右键菜单按钮public override void BuildContextualMenu(ContextualMenuPopulateEvent evt){var types = TypeCache.GetTypesDerivedFrom<Node>();foreach (var type in types){evt.menu.AppendAction($"创建节点/{type.Name}", a => CreateNode(type));}evt.menu.AppendAction("删除选中元素", DeleteSelecteNode);}//删除选中元素,节点或者连线private void DeleteSelecteNode(DropdownMenuAction action){DeleteSelection();}//创建节点private void CreateNode(Type type){Node node = tree.CreateNode(type);CreateNodeView(node);}private void CreateNodeView(Node node){NodeView nodeView = new NodeView(node);nodeView.OnNodeSelected = OnNodeSelected;AddElement(nodeView);}

3、设置节点元素输出端可连接端口

public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter){return ports.ToList().Where(endpost => endpost.direction != startPort.direction && endpost.node != startPort.node).ToList();}

4、打开或者重新展示已有内容

internal void PopulateView(NodeTree tree){this.tree = tree;graphViewChanged -= OnGraphViewChange;DeleteElements(graphElements.ToList());graphViewChanged += OnGraphViewChange;tree.nodes.ForEach(n => CreateNodeView(n));tree.nodes.ForEach(n =>{var children = tree.GetChildren(n);children.ForEach(c =>{NodeView parentView = FindNodeView(n);NodeView childView = FindNodeView(c);Edge edge = parentView.output.ConnectTo(childView.input);AddElement(edge);});});}

5、当节点区域元素改变时,实现对应逻辑数据的修改

该方法在打开或展现时注册事件

private GraphViewChange OnGraphViewChange(GraphViewChange graphViewChange){if(graphViewChange.elementsToRemove != null){graphViewChange.elementsToRemove.ForEach(elem => { NodeView nodeview = elem as NodeView;if(nodeview != null){tree.DeleteNode(nodeview.node);}Edge edge = elem as Edge;if(edge != null){NodeView parentView = edge.output.node as NodeView;NodeView childView = edge.input.node as NodeView;tree.RemoveChild(parentView.node, childView.node);}});}if(graphViewChange.edgesToCreate != null){graphViewChange.edgesToCreate.ForEach(edge =>{NodeView parentView = edge.output.node as NodeView;NodeView childView = edge.input.node as NodeView;tree.AddChild(parentView.node, childView.node);});}return graphViewChange;}

6、完整代码

运行时代码Runtime Code

6.1 Node

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public abstract class Node : ScriptableObject
{public enum State { Running, Waiting}public State state = State.Waiting;public bool started = false;public List<Node> children = new List<Node>();[HideInInspector] public string guid;[HideInInspector] public Vector2 position;public Node OnUpdate(){if(!started){OnStart();started = true;}Node currentNode = LogicUpdate();if(state != State.Running){OnStop();started = false;}return currentNode;}public abstract Node LogicUpdate();public abstract void OnStart();public abstract void OnStop();}

6.2 NormalNode

using System.Collections;
using System.Collections.Generic;
using UnityEngine;[CreateAssetMenu]
public class NormalNode : Node
{[TextArea]public string dialogueContent;public override Node LogicUpdate(){// 判断进入下一节点条件成功时 需将节点状态改为非运行中 且 返回对应子节点if (Input.GetKeyDown(KeyCode.Space)){state = State.Waiting;if (children.Count > 0){children[0].state = State.Running;return children[0];}}return this;}public override void OnStart(){Debug.Log(dialogueContent);}public override void OnStop(){Debug.Log("OnStop");}
}

6.3 NodeTree

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;[CreateAssetMenu]
public class NodeTree : ScriptableObject
{public Node rootNode;public Node runningNode;public Node.State treeState = Node.State.Waiting;public List<Node> nodes = new List<Node>();public virtual void Update(){if(treeState == Node.State.Running && runningNode.state == Node.State.Running){runningNode = runningNode.OnUpdate();}}/// <summary>/// 对话树开始的触发方法/// </summary>public virtual void OnTreeStart(){treeState = Node.State.Running;runningNode.state = Node.State.Running;}/// <summary>/// 对话树结束的触发方法/// </summary>public void OnTreeEnd(){treeState = Node.State.Waiting;}#if UNITY_EDITORpublic Node CreateNode(System.Type type){Node node = ScriptableObject.CreateInstance(type) as Node;node.name = type.Name;node.guid = GUID.Generate().ToString();nodes.Add(node);if (!Application.isPlaying){AssetDatabase.AddObjectToAsset(node, this);}AssetDatabase.SaveAssets();return node;}public Node DeleteNode(Node node){nodes.Remove(node);AssetDatabase.RemoveObjectFromAsset(node);AssetDatabase.SaveAssets();return node;}public void RemoveChild(Node parent, Node child){parent.children.Remove(child);}public void AddChild(Node parent, Node child){parent.children.Add(child);}public List<Node> GetChildren(Node parent){return parent.children;}
#endif
}

6.4 NodeTreeRunner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NodeTreeRunner : MonoBehaviour
{public NodeTree tree;void Start(){}void Update(){if(Input.GetKeyDown(KeyCode.P)){tree.OnTreeStart();}if(tree != null && tree.treeState == Node.State.Running){tree.Update();}if(Input.GetKeyDown(KeyCode.D)){tree.OnTreeEnd();}}
}

可视化编辑器代码 Editor

6.5 Uxml和Uss

NodeEditor Uxml

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True"><Style src="NodeEditor.uss" /><ui:VisualElement style="flex-grow: 1; flex-direction: row;"><ui:VisualElement name="LeftDiv" style="flex-grow: 0.3;"><ui:Label text="Inspector" display-tooltip-when-elided="true" name="Inspector" style="font-size: 15px; -unity-font-style: bold;" /><uie:ObjectField label="NodeTree" name="NodeTree" style="flex-grow: 0; flex-shrink: 0; min-width: auto; align-items: stretch; flex-wrap: nowrap; flex-direction: row; width: auto; max-width: none;" /><InspectorViewer style="flex-grow: 1;" /></ui:VisualElement><ui:VisualElement name="RightDiv" style="flex-grow: 0.7;"><ui:Label text="NodeTreeVirwer" display-tooltip-when-elided="true" name="NodeTreeVirwer" style="-unity-font-style: bold; font-size: 15px;" /><NodeTreeViewer focusable="true" style="flex-grow: 1;" /></ui:VisualElement></ui:VisualElement>
</ui:UXML>

NodeTreeViewer Uss

GridBackground{--grid-background-color: rgb(40,40,40);--line-color: rgba(193,196,192,0.1);--thick-line-color: rgba(193,196,192,0.1);--spacing: 15;
}

编辑器面板代码

6.6 NodeEdtor

using System;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;public class NodeEditor : EditorWindow
{public NodeTreeViewer nodeTreeViewer;public InspectorViewer inspectorViewer;public ObjectField nodeTreeObj;[MenuItem("MyWindows/NodeEditor")]public static void ShowExample(){NodeEditor wnd = GetWindow<NodeEditor>();wnd.titleContent = new GUIContent("NodeEditor");}public void CreateGUI(){// Each editor window contains a root VisualElement objectVisualElement root = rootVisualElement;// Import UXMLvar visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/NodeEditor/Editor/UI/NodeEditor.uxml");visualTree.CloneTree(root);var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/NodeEditor/Editor/UI/NodeEditor.uss");root.styleSheets.Add(styleSheet);nodeTreeViewer = root.Q<NodeTreeViewer>();inspectorViewer = root.Q<InspectorViewer>();nodeTreeObj = root.Q("NodeTree") as ObjectField;nodeTreeObj.objectType = typeof(NodeTree);nodeTreeViewer.OnNodeSelected = OnNodeSelectionChanged;}private void OnNodeSelectionChanged(NodeView view){inspectorViewer.UpdateSelection(view.node);}private void OnSelectionChange(){NodeTree tree = Selection.activeObject as NodeTree;if (tree){nodeTreeViewer.PopulateView(tree);nodeTreeObj.value = tree;}else{nodeTreeViewer.CloseNodeTreeViewer();nodeTreeObj.value = null;}}}

6.7 NodeTreeViewer 

using BehaviorDesigner.Runtime.Tasks.Unity.UnityInput;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;public class NodeTreeViewer : GraphView
{public new class UxmlFactory : UxmlFactory<NodeTreeViewer, UxmlTraits> { }public NodeTree tree;public Action<NodeView> OnNodeSelected;private Vector2 curMousePos;ContentZoomer contentZoomer;ContentDragger contentDragger;public NodeTreeViewer(){Insert(0, new GridBackground());contentZoomer = new ContentZoomer();this.AddManipulator(contentZoomer);contentDragger = new ContentDragger();this.AddManipulator(contentDragger);this.AddManipulator(new SelectionDragger());this.AddManipulator(new RectangleSelector());var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/NodeEditor/Editor/UI/NodeTreeViewer.uss");styleSheets.Add(styleSheet);this.RegisterCallback<MouseDownEvent>(OnMouseDown);}private void OnMouseDown(MouseDownEvent evt){Debug.Log(evt.localMousePosition);curMousePos = evt.localMousePosition;Debug.Log(contentZoomer.scaleStep);Debug.Log(contentZoomer.referenceScale);//Debug.Log(contentDragger.p)}public override void BuildContextualMenu(ContextualMenuPopulateEvent evt){var types = TypeCache.GetTypesDerivedFrom<Node>();foreach (var type in types){evt.menu.AppendAction($"创建节点/{type.Name}", a => CreateNode(type));}evt.menu.AppendAction("删除选中元素", DeleteSelecteNode);}private void DeleteSelecteNode(DropdownMenuAction action){DeleteSelection();}private void CreateNode(Type type){Node node = tree.CreateNode(type);node.position = curMousePos;CreateNodeView(node);}private void CreateNodeView(Node node){NodeView nodeView = new NodeView(node);nodeView.OnNodeSelected = OnNodeSelected;AddElement(nodeView);}internal void PopulateView(NodeTree tree){this.tree = tree;graphViewChanged -= OnGraphViewChange;DeleteElements(graphElements.ToList());graphViewChanged += OnGraphViewChange;tree.nodes.ForEach(n => CreateNodeView(n));tree.nodes.ForEach(n =>{var children = tree.GetChildren(n);children.ForEach(c =>{NodeView parentView = FindNodeView(n);NodeView childView = FindNodeView(c);Edge edge = parentView.output.ConnectTo(childView.input);AddElement(edge);});});}public void CloseNodeTreeViewer(){this.tree = null;graphViewChanged -= OnGraphViewChange;DeleteElements(graphElements.ToList());}private GraphViewChange OnGraphViewChange(GraphViewChange graphViewChange){if(graphViewChange.elementsToRemove != null){graphViewChange.elementsToRemove.ForEach(elem => { NodeView nodeview = elem as NodeView;if(nodeview != null){tree.DeleteNode(nodeview.node);}Edge edge = elem as Edge;if(edge != null){NodeView parentView = edge.output.node as NodeView;NodeView childView = edge.input.node as NodeView;tree.RemoveChild(parentView.node, childView.node);}});}if(graphViewChange.edgesToCreate != null){graphViewChange.edgesToCreate.ForEach(edge =>{NodeView parentView = edge.output.node as NodeView;NodeView childView = edge.input.node as NodeView;tree.AddChild(parentView.node, childView.node);});}return graphViewChange;}NodeView FindNodeView(Node node){return GetNodeByGuid(node.guid) as NodeView;}public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter){return ports.ToList().Where(endpost => endpost.direction != startPort.direction && endpost.node != startPort.node).ToList();}}

6.8 NodeView

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;public class NodeView : UnityEditor.Experimental.GraphView.Node
{public Node node;public Port input;public Port output;public Action<NodeView> OnNodeSelected;public NodeView(Node node){this.node = node;this.title = node.name;this.viewDataKey = node.guid;style.left = node.position.x;style.top = node.position.y;CreateInputPorts();CreateOutputPorts();}private void CreateInputPorts(){input = InstantiatePort(Orientation.Vertical, Direction.Input, Port.Capacity.Multi, typeof(bool));if(input != null ){input.portName = "input";inputContainer.Add(input);}}private void CreateOutputPorts(){output = InstantiatePort(Orientation.Vertical, Direction.Output, Port.Capacity.Multi, typeof(bool));if (output != null){output.portName = "output";outputContainer.Add(output);}}public override void OnSelected(){base.OnSelected();if( OnNodeSelected != null ){OnNodeSelected?.Invoke(this);}}public override void SetPosition(Rect newPos){Debug.Log(newPos);base.SetPosition(newPos);node.position.x = newPos.xMin;node.position.y = newPos.yMin;}}

6.9 InspectorViewer

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;public class InspectorViewer : VisualElement
{public new class UxmlFactory : UxmlFactory<InspectorViewer, UxmlTraits> { }Editor editor;public InspectorViewer(){//this.AddManipulator(new drag)}internal void UpdateSelection(Node node){Clear();UnityEngine.Object.DestroyImmediate(editor);editor = Editor.CreateEditor(node);IMGUIContainer container = new IMGUIContainer(() =>{if (editor.target){editor.OnInspectorGUI();}});Add(container);}}

【Unity UIToolkit】UIBuilder基础教程-制作简易的对话系统编辑器 3步教你玩转Unity编辑器扩展工具_unity uibuilder-CSDN博客

[Unity] GraphView 可视化节点的事件行为树(二) UI Toolkit介绍,制作事件行为树的UI_unity graphview-CSDN博客 

相关文章:

【Unity编辑器拓展】GraphView自定义可视化节点

1、创建节点区域脚本 其中的new class UxmlFactory&#xff0c;可以让该元素显示在UI Builder中&#xff0c;我们就可以在Library-Project中看到我们新建的这两个UI元素&#xff0c;就可以拖入我们的UI窗口编辑了 public class NodeTreeViewer : GraphView {public new class…...

教程系列4 | 趋动云『社区项目』极速体验 LivePortrait 人脸表情“移花接木”大法

LivePortrait LivePortrait 由快手可灵大模型团队开源&#xff0c;只需 1 张原图就能生成动态视频。 LivePortrait 的核心优势在于其卓越的表情"迁移"技术&#xff0c;能够令静态图像中的人物瞬间焕发活力&#xff0c;无论是眨眼、微笑还是转头&#xff0c;皆栩栩如…...

WGS84、GCJ-02、BD09三大坐标系详解

文章目录 前言WGS84坐标系定义应用WGS84 Web 墨卡托投影 GCJ-02坐标系&#xff08;火星坐标系&#xff09;定义应用GCJ-02经纬度投影与Web墨卡托投影 BD09坐标系&#xff08;百度坐标系&#xff09;定义应用BD09经纬度投影与Web墨卡托投影 坐标系之间的区别与注意事项总结 前言…...

css上下动画 和淡化

.popup_hidden_bg { transition: opacity .5s ease-out; opacity: 0; pointer-events: none; /* 防止在隐藏时仍然能点击 */ } keyframes popupShop { from { transform: translateY(100%); opacity: 0; } to {transform: translateY(0);opacity: 1; }} keyframes popupHidd…...

深入解析C#中的URI和URL编码:理解EscapeDataString、EscapeUriString和UrlEncode的区别及字符编码错误处理

在C#中&#xff0c;处理URI&#xff08;统一资源标识符&#xff09;和URL&#xff08;统一资源定位符&#xff09;时&#xff0c;可以使用Uri.EscapeDataString、Uri.EscapeUriString和HttpUtility.UrlEncode&#xff08;或WebUtility.UrlEncode&#xff09;方法来编码字符串。…...

【CSS】给图片设置 max-width

.logo img{width:100%; /* 缩成父盒子的100% */max-width:100%; /* (谁小用谁的百分之百) *//* max-width:100%;【1】图片比盒子大&#xff0c;缩成父盒子的100%【2】图片比盒子小&#xff0c;图片自身的100%*/ }示例 设置样式 .el-image {width: 100%;max-width: 100%;max-…...

区块链——代码格式检查(prettier、solhint)

一、引入依赖 // 导入prettier prettier-plugin-solidity yarn add --dev prettier prettier-plugin-solidity yarn add --dev solhint二、创建.prettierrc文件 {"tabWidth": 2,"semi": false,"useTabs": false,"singleQuote": fals…...

搭建自动化 Web 页面性能检测系统 —— 部署篇

作为一个前端想去做全栈的项目时&#xff0c;可能第一个思路是 node vue/react。一开始可能会新建多个工程目录去实现&#xff0c;假设分别为 web 和 server&#xff0c;也许还有管理后台的代码 admin&#xff0c;那么就有了三个工程的代码。此时为了方便管理就需要在远程仓库…...

知识图谱增强的RAG(KG-RAG)详细解析

转自&#xff1a;知识图谱科技 这是一个与任务无关的框架&#xff0c;它将知识图谱&#xff08;KG&#xff09;的显性知识与大型语言模型&#xff08;LLM&#xff09;的隐含知识结合起来。这是该工作的arXiv预印本 https://arxiv.org/abs/2311.17330 。 我们在这里利用一个名为…...

python中list的深拷贝和浅拷贝

其实这还是涉及到python中的可变对象和不可变对象的概念。 https://www.cnblogs.com/poloyy/p/15073168.html # -*- coding: utf-8 -*-person [name, [savings, 100.00]] hubby person[:] # slice copy wifey list(person) # fac func copy a [id(x) for x in person] b …...

【LeetCode】字母异位词分组

题目描述&#xff1a; 给你一个字符串数组&#xff0c;请你将字母异位词组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [[“bat”…...

Golang | Leetcode Golang题解之第295题数据流的中位数

题目&#xff1a; 题解&#xff1a; type MedianFinder struct {nums *redblacktree.Treetotal intleft, right iterator }func Constructor() MedianFinder {return MedianFinder{nums: redblacktree.NewWithIntComparator()} }func (mf *MedianFinder) AddNum(…...

【C语言】C语言期末突击/考研--数据的输入输出

目录 一、printf()输出函数介绍 二、scanf读取标准输入 &#xff08;一&#xff09;scanf函数的原理 &#xff08;二&#xff09;多种数据类型混合输入 三、练习题 今天我们学习printf和scanf读取标准输入。下面我们开始正式的学习吧。 C语言中有很多内置函数&#xff0c;今…...

How can I fix my Flask server‘s 405 error that includes OpenAi api?

题意&#xff1a;解决包含OpenAI API的Flask服务器中出现的405错误&#xff08;Method Not Allowed&#xff0c;即方法不允许&#xff09; 问题背景&#xff1a; Im trying to add an API to my webpage and have never used any Flask server before, I have never used Java…...

LeetCode Hot100 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确…...

【Linux】线程的控制

目录 线程等待 线程退出 线程的优缺点 线程独占和共享的数据 我们说Linux是用进程模拟的线程&#xff0c;所以Linux中只有轻量级进程的概念&#xff0c;但是&#xff0c;用户是只认线程的&#xff0c;所以我们有一个叫原生线程库的东西&#xff0c;它就负责把轻量级进程的系…...

Vue3自研开源Tree组件:人性化的拖拽API设计

针对Element Plus Tree组件拖拽功能API用的麻烦&#xff0c;小卷开发了一个API使用简单的JuanTree组件。拖拽功能用起来非常简单&#xff01; 文章目录 使用示例allowDragallowDrop支持节点勾选支持dirty检测后台API交互 源码实现 使用示例 组件的使用很简单&#xff1a; 通过…...

MYSQL--触发器详解

触发器 1.触发器简介 触发器&#xff08;trigger&#xff09;是一个特殊的存储过程&#xff0c;它的执行不是由程序调用&#xff0c;也不是手工启动&#xff0c;而是由事件来触发&#xff0c;比如当对一个表进行操作&#xff08; insert&#xff0c;delete&#xff0c; update…...

C++实用指南:Lambda 表达式的妙用

Lambda 表达式的灵活性和强大功能确实为编程提供了许多便利。但是我们发现许多开发者仍然无法灵活运用其便利&#xff0c;于是写了这篇文章。 Lambda 允许我们编写更简洁和灵活的代码。例如在处理网络请求时&#xff0c;我们经常需要确保响应与当前的状态或需求仍然相关。通过…...

FastAPI(七十八)实战开发《在线课程学习系统》接口开发-- 评论

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 梳理下思路 1.判断是否登录 2.课程是否存在 3.如果是回复&#xff0c;查看回复是否存在 4.是否有权限 5.发起评论 首先新增pydantic模型 class Cour…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

Linux操作系统共享Windows操作系统的文件

目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项&#xff0c;设置文件夹共享为总是启用&#xff0c;点击添加&#xff0c;可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download&#xff08;这是我共享的文件夹&#xff09;&…...