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

Unity编辑器拓展-Odin

1.相比于原生Unity的优势

  • Unity不支持泛型类型序列化,例如字典原生Unity不支持序列化,而Odin可以继承序列化的Mono实现
  • 功能强大且使用简单,原生Unity想实现一些常见的功能需要额外自己编写Unity扩展的编码,实现功能只需要加一个特性即可Odin帮忙写好了内部管理和实现
  • 编辑器的窗口实现简单且美观

2.常用功能代码总结

通过Tools-Odin Inspector-Attribute Overview即可打开一个预览各个特性的效果的窗口,可供参考

OdinValueDrawer

类继承自OdinValueDrawer,其中T为自定义数据类型,之后重写DrawPropertyLayout方法,实现自定义绘制,实现了对于我们自定义数据类型的自定义绘制,重写Initialize来做对其下序列化部分的初始化,例如Selector的生成,选择变化的事件监听,初始值的定义。同时在DrawPropertyLayout中定义对应的按钮,在按下的时候用selector.ShowInPopup();打开对应的selector

#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos
{using UnityEngine;using System;#if UNITY_EDITORusing Sirenix.OdinInspector.Editor;using UnityEditor;using Sirenix.Utilities;#endif// 演示如何为自定义类型生成自定义drawer的示例。[TypeInfoBox("此示例演示如何为自定义结构或类实现自定义drawer")]public class CustomDrawerExample : MonoBehaviour{public MyStruct MyStruct;[ShowInInspector]public static float labelWidth = 10;}// 自定义数据结构,用于演示。[Serializable]public struct MyStruct{public float X;public float Y;}#if UNITY_EDITORpublic class CustomStructDrawer : OdinValueDrawer<MyStruct>{protected override void DrawPropertyLayout(GUIContent label){//获取我们绘制类的值MyStruct value = this.ValueEntry.SmartValue;//获取要绘制的区域(rect)var rect = EditorGUILayout.GetControlRect();//在Odin中,标签是可选项,可以为空,所以我们必须考虑到这一点。if (label != null){rect = EditorGUI.PrefixLabel(rect, label);}//保存原始labelWidth的宽度,此label为struct中对应的X,Yvar prev = EditorGUIUtility.labelWidth;//设定新的label宽度EditorGUIUtility.labelWidth = CustomDrawerExample.labelWidth;//根据slider对应的值进行赋值value.X = EditorGUI.Slider(rect.AlignLeft(rect.width * 0.5f), "X", value.X, 0, 1);value.Y = EditorGUI.Slider(rect.AlignRight(rect.width * 0.5f), "Y", value.Y, 0, 1);//恢复设定原始label宽度EditorGUIUtility.labelWidth = prev;//将新的Struct赋值给我们定义的MyStructthis.ValueEntry.SmartValue = value;}}
#endif
}
#endif

OdinSelector

一个选择框,可以选择提供的列表,同时选择后触发对应的事件

BuildSelectionTree

BuildSelectionTree的作用是在OdinSelector中构建一个选择树,也就是显示在弹出窗口中的树形结构的列表。你可以通过重写这个方法,来决定选择树中包含哪些值,以及它们的层级关系和排序方式。

protected override void BuildSelectionTree(OdinMenuTree tree)  
{  
//搜索框tree.Config.AutoFocusSearchBar = true;  tree.Config.DrawSearchToolbar = true;  tree.Selection.SupportsMultiSelect = false;  tree.MenuItems.Clear();  foreach (结合excel枚举对应的pb)  {      tree.Add(path, instance);}}

在外部进行的初始化

例如在上述的Drawer的Init中进行初始化,需要new出这个类,并且增加监听事件,且设置初始值。同时监听外部按钮,打开这个selector

m_WeaponIDSelector = new WeaponIDSelector();  
m_WeaponIDSelector.SelectionTree.UpdateMenuTree();  
m_WeaponIDSelector.SetSelection(WeaponData.weaponKey.thingID); //初始值 
m_WeaponIDSelector.SelectionChanged += OnWeaponThingIDChanged;//改变时候监听
// 重写Initialize方法
protected override void Initialize()
{
// 调用基类的Initialize方法
base.Initialize();// 实例化OdinMenuTree对象
tree = new OdinMenuTree();// 添加一些菜单项到选择树中,例如一些GameObject或者其他自定义对象
tree.Add("Cube", GameObject.CreatePrimitive(PrimitiveType.Cube));
tree.Add("Sphere", GameObject.CreatePrimitive(PrimitiveType.Sphere));
tree.Add("MyObject", new MyObject());// 设置选择树的配置,例如是否支持多选,是否显示搜索栏等
tree.Config.DrawSearchToolbar = true;
tree.Config.MultiSelect = true;// 设置选择树的事件,例如当菜单项被选中或者双击时执行一些操作
tree.Selection.SelectionChanged += OnSelectionChanged;
tree.MenuItems.OnDoubleClick += OnDoubleClick;// 在Initialize的时候调用UpdateMenuTree方法,以便初始化选择树中的菜单项,以及处理搜索和排序等功能
tree.UpdateMenuTree();
}

在外部按钮按下进行的显示这个selector

//FocusType是一个枚举类型,用于表示按钮是否可以通过键盘选择。FocusType有三个可能的值:Passive、Keyboard和Native.Passive表示按钮不会接收键盘焦点,只能通过鼠标点击选择;Keyboard表示按钮可以通过Tab键或方向键在其他控件之间切换焦点;Native表示按钮使用平台本地的焦点行为,例如在Mac上使用Option+Tab键切换焦点.在你的代码中,FocusType.Passive表示你的下拉按钮不需要通过键盘选择,只能通过鼠标点击打开下拉菜单。
if (EditorGUILayout.DropdownButton(new GUIContent("武器选择: " + m_WeaponIDSelector.CurSelectName), FocusType.Passive))  
{  m_WeaponIDSelector.RebuildSelectionTree();  m_WeaponIDSelector.EnsureSingleClickToSelect();  m_WeaponIDSelector.ShowInPopup();  
}
public void RebuildSelectionTree()  
{  List<uint> curSelections = GetCurrentSelection().ToList();  BuildSelectionTree(SelectionTree);  EnableIDChangeEvent = false;  SetSelection(curSelections);  EnableIDChangeEvent = true;  SelectionTree.Config.AutoFocusSearchBar = true;  
}
public void EnsureSingleClickToSelect()  
{  
//设置选择树的Config.SelectionConfirmNavigationKey属性为KeyCode.None,这样就可以取消双击确认选择的功能EnableSingleClickToSelect();  //设置了SelectionTree.Config.ConfirmSelectionOnDoubleClick属性为false,这个属性也是用于控制是否需要双击确认选择的功能虽然这个属性和Config.SelectionConfirmNavigationKey属性有重复的作用,但是为了保险起见,最好都设置一下。SelectionTree.Config.ConfirmSelectionOnDoubleClick = false;  //设置了SelectionTree.Config.AutoFocusSearchBar属性为true,这个属性可以让选择树在打开时自动聚焦搜索栏,方便用户输入搜索关键词SelectionTree.Config.AutoFocusSearchBar = true;  
}

处理Selector的改变

这里需要注意的是要用var first = col.FirstOrDefault(); 来取,并且需要做判空,一般是取完keyID之后再去读表刷上其他的数据,或者是根据

selector.SelectionChanged += col => //添加SelectionChanged事件的处理方法
{
var first = col.FirstOrDefault(); //获取第一个选择的对象
if (first != null) //如果不为空
{
selectedName = first.name; //更新selectedName为对象的名称
}

缩进级别

  • GUIHelper.PushIndentLevel(1);
  • GUIHelper.PopIndentLevel();
    结合使用,增加一个缩进级别,可以让GUI的元素更加有层次感和结构化。例如,你可以用这个方法来创建一个树形的菜单或列表。

结构化API

  • EditorGUILayout.BeginHorizontal();这两个老朋友了就不解释了
  • EditorGUILayout.BeginVertical();
  • GUIHelper.PushGUIEnabled(bool);//表示接下来的一个范围内我要检查一下这个bool值来觉得这些UI是否可以被选
  • GUIHelper.PopGUIEnabled();
//GUIHelper.PushGUIEnabled和GUIHelper.PopGUIEnabled是两个用于控制GUI元素是否可用的方法。它们的作用是在一段代码中临时改变GUI元素的可用状态,而不影响其他代码中的GUI元素。具体来说,GUIHelper.PushGUIEnabled方法可以接受一个布尔值作为参数,表示要设置的GUI元素的可用状态。这个方法会将当前的GUI元素的可用状态压入一个栈中,然后设置新的可用状态。GUIHelper.PopGUIEnabled方法则会从栈中弹出之前保存的GUI元素的可用状态,并恢复它。这样就可以在一段代码中临时禁用或启用一些GUI元素,而不影响其他代码中的GUI元素。
GUIHelper.PushGUIEnabled(toggle); //根据复选框的状态设置按钮的可用状态
if (GUILayout.Button("Click Me")) //绘制按钮会受到是否可选的影响
{
Debug.Log("You clicked the button!"); //打印日志
}
GUIHelper.PopGUIEnabled(); //恢复之前的可用状态

补充

可以通过UnityToolbarExtender来打开目标窗口,使用起来也非常简单

static ToolbarGUIUtility()  
{  uxToolPrefabOpen = EditorPrefs.GetBool("UXToolPrefabOpen", false);  uxToolPowerOpen = EditorPrefs.GetBool("UXToolPowerOpen", false);  ToolbarExtender.LeftToolbarGUI.Add(OnLeftToolbarGUI);  ToolbarExtender.RightToolbarGUI.Add(OnRightToolbarGUI);  
}
private static GUIStyle ToolbarButtonLeftStyle  
{  get  {  if (toolbarButtonLeftStyle == null) toolbarButtonLeftStyle = new GUIStyle("toolbarbuttonLeft");  return toolbarButtonLeftStyle;  }}
static void OnLeftToolbarGUI()  
{  if (TestCommon.TestHelper.ColorButton(new GUIContent("XXX配置", "XXXToolTip"), new Color(0.55f, 0.37f, 1f), GUILayoutOptions.Width(90), ToolbarButtonLeftStyle))  {        TestWindow.OpenWindow();  }

快速创建Button的管理者类

#if UNITY_EDITOR  
using System;  
using System.Collections.Generic;  
using System.Reflection;  
using Sirenix.Utilities;  
using Sirenix.Utilities.Editor;  
using UnityEditor;  
using UnityEngine;  namespace TestCommon
{  public static class TestHelper{  private static GUIStyle ColoredButtonStyle = new GUIStyle(GUI.skin.button);  public static bool ColorButton(string text, Color color, int width, int height, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  Rect buttonRect = GUILayoutUtility.GetRect(width, height);  bool res = guiStyle != null ? GUI.Button(buttonRect, "", guiStyle) : GUI.Button(buttonRect, "");  GUIHelper.PopColor();  GUI.Label(buttonRect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(string text, Color color, GUILayoutOption[] guiLayoutOptions, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  GUIHelper.PushContentColor(Color.clear);  bool res = guiStyle != null ? GUILayout.Button(text, guiStyle, guiLayoutOptions) : GUILayout.Button(text, guiLayoutOptions);  GUIHelper.PopContentColor();  GUIHelper.PopColor();  Rect buttonRect = GUILayoutUtility.GetLastRect();  GUI.Label(buttonRect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(GUIContent guiContent, Color color, GUILayoutOption[] guiLayoutOptions, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            ColoredButtonStyle.normal.textColor = Color.white;  GUIHelper.PushColor(color);  GUIHelper.PushContentColor(Color.clear);  bool res = guiStyle != null ? GUILayout.Button(guiContent, guiStyle, guiLayoutOptions) : GUILayout.Button(guiContent, guiLayoutOptions);  GUIHelper.PopContentColor();  GUIHelper.PopColor();  Rect buttonRect = GUILayoutUtility.GetLastRect();  GUI.Label(buttonRect, guiContent.text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(Rect rect, string text, Color color, GUIStyle guiStyle = null, GUIStyle labelGUIStyle = null)  {            GUIHelper.PushColor(color);  bool res = guiStyle != null ? GUI.Button(rect, "", guiStyle) : GUI.Button(rect, "");  GUIHelper.PopColor();  GUI.Label(rect, text, labelGUIStyle ?? SirenixGUIStyles.LabelCentered);  return res;  }  public static bool ColorButton(Rect rect, Texture2D texture2D, Color color, GUIStyle guiStyle = null)  {            GUIHelper.PushColor(color);  bool res = guiStyle != null ? GUI.Button(rect, texture2D, guiStyle) : GUI.Button(rect, texture2D);  GUIHelper.PopColor();  return res;  }  public static void RichLabel(Rect rect, string text, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null)  {            string richText = text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  if (guiStyle != null)  GUI.Label(rect, richText, guiStyle);  else  GUI.Label(rect, richText);  }  public static void RichLabel(Rect rect, GUIContent guiContent, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null)  {            string richText = guiContent.text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  guiContent.text = richText;  if (guiStyle != null)  GUI.Label(rect, guiContent, guiStyle);  else  GUI.Label(rect, guiContent);  }  public static void RichLabel(string text, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null, GUILayoutOption[] guiLayoutOptions = null)  {            string richText = text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  if (guiStyle != null)  {                if (guiLayoutOptions != null)  GUILayout.Label(richText, guiStyle, guiLayoutOptions);  else  GUILayout.Label(richText, guiStyle);  }            else  {  if (guiLayoutOptions != null)  GUILayout.Label(richText, guiLayoutOptions);  else  GUILayout.Label(richText);  }        }  public static void RichLabel(GUIContent guiContent, Color color, int fontSize = 12, bool italic = false, bool bold = false, GUIStyle guiStyle = null, GUILayoutOption[] guiLayoutOptions = null)  {            string richText = guiContent.text;  richText = $"<size={fontSize}><color=#{ColorUtility.ToHtmlStringRGB(color)}>{richText}</color></size>";  if (italic) richText = $"<i>{richText}</i>";  if (bold) richText = $"<b>{richText}</b>";  guiContent.text = richText;  if (guiStyle != null)  {                if (guiLayoutOptions != null)  GUILayout.Label(guiContent, guiStyle, guiLayoutOptions);  else  GUILayout.Label(guiContent, guiStyle);  }            else  {  if (guiLayoutOptions != null)  GUILayout.Label(guiContent, guiLayoutOptions);  else  GUILayout.Label(guiContent);  }        }  public enum Alignment  {  UpperLeft,  UpperCenter,  UpperRight,  MiddleLeft,  MiddleCenter,  MiddleRight,  BottomLeft,  BottomCenter,  BottomRight,  }  public static bool IsUpper(this Alignment alignment)  {            return alignment == Alignment.UpperRight || alignment == Alignment.MiddleRight || alignment == Alignment.BottomRight;  }  public static bool IsMiddle(this Alignment alignment)  {            return alignment == Alignment.MiddleLeft || alignment == Alignment.MiddleCenter || alignment == Alignment.MiddleRight;  }  public static bool IsBottom(this Alignment alignment)  {            return alignment == Alignment.BottomLeft || alignment == Alignment.BottomCenter || alignment == Alignment.BottomRight;  }  public static bool IsLeft(this Alignment alignment)  {            return alignment == Alignment.UpperLeft || alignment == Alignment.MiddleLeft || alignment == Alignment.BottomLeft;  }  public static bool IsRight(this Alignment alignment)  {            return alignment == Alignment.UpperRight || alignment == Alignment.MiddleRight || alignment == Alignment.BottomRight;  }  public static bool IsCenter(this Alignment alignment)  {            return alignment == Alignment.UpperCenter || alignment == Alignment.MiddleCenter || alignment == Alignment.BottomCenter;  }  public static void DrawResponsiveLayout(int rowWidth, List<int> elementWidths, Action<int> drawActions, Alignment alignment)  {            int accumulatedWidth = 0;  GUILayout.BeginVertical(GUILayoutOptions.ExpandWidth(false).ExpandHeight(false));  {                if (alignment.IsMiddle() || alignment.IsBottom()) GUILayout.FlexibleSpace();  GUILayout.BeginHorizontal();  if (alignment.IsRight() || alignment.IsCenter()) GUILayout.FlexibleSpace();  for (int index = 0; index < elementWidths.Count; index++)  {                    int elementWidth = elementWidths[index] + 4;  if (accumulatedWidth + elementWidth > rowWidth)  {                        accumulatedWidth = 0;  if (alignment.IsLeft() || alignment.IsCenter()) GUILayout.FlexibleSpace();  GUILayout.EndHorizontal();  GUILayout.BeginHorizontal();  if (alignment.IsRight() || alignment.IsCenter()) GUILayout.FlexibleSpace();  }  accumulatedWidth += elementWidth;                    drawActions.Invoke(index);  }  if (alignment.IsLeft() || alignment.IsCenter()) GUILayout.FlexibleSpace();  GUILayout.EndHorizontal();  if (alignment.IsMiddle() || alignment.IsUpper()) GUILayout.FlexibleSpace();  }            GUILayout.EndVertical();  }  [InitializeOnLoad]  public class ScrollableTextArea  {  private delegate string ScrollableTextAreaInternalDelegate(  Rect position,  string text,  ref Vector2 scrollPosition,  GUIStyle style);  private static readonly ScrollableTextAreaInternalDelegate EditorGUI_ScrollableTextAreaInternal;  static ScrollableTextArea()  {                MethodInfo method = typeof(EditorGUI).GetMethod("ScrollableTextAreaInternal", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);  if (method == null)  return;  EditorGUI_ScrollableTextAreaInternal = (ScrollableTextAreaInternalDelegate)Delegate.CreateDelegate(typeof(ScrollableTextAreaInternalDelegate), method);  }  public static string EditorGUITextArea(Rect rect, string text, ref Vector2 scrollPos, int minLines = 0, int maxLines = 10)  {                return EditorGUI_ScrollableTextAreaInternal(rect, text, ref scrollPos, EditorStyles.textArea);  }  public static string EditorGUILayoutTextArea(string text, ref Vector2 scrollPos, int minLines = 0, int maxLines = 10)  {                float height = 32f + (float)((Mathf.Clamp(Mathf.CeilToInt(EditorStyles.textArea.CalcHeight(GUIHelper.TempContent(text), GUIHelper.ContextWidth) / 13f), minLines, maxLines) - 1) * 13);  Rect controlRect = EditorGUILayout.GetControlRect(false, height);  return EditorGUI_ScrollableTextAreaInternal(controlRect, text, ref scrollPos, EditorStyles.textArea);  }        }  public static Rect BeginColorBox(string label, Color color, params GUILayoutOption[] options)  {            Rect rect = SirenixEditorGUI.BeginBox(options);  SirenixEditorGUI.DrawSolidRect(rect, color);  SirenixEditorGUI.DrawBorders(rect, 1, new Color(0.24f, 0.24f, 0.24f));  SirenixEditorGUI.BeginBoxHeader();  float fieldWidth = EditorGUIUtility.fieldWidth;  EditorGUIUtility.fieldWidth = 10f;  Rect controlRect = EditorGUILayout.GetControlRect(false);  EditorGUIUtility.fieldWidth = fieldWidth;  GUI.Label(controlRect, label);  SirenixEditorGUI.EndBoxHeader();  return rect;  }  public static void EndColorBox()  {            SirenixEditorGUI.EndBox();  }  public static Color Grey(float value)  {            return new Color(value, value, value);  }  public static Color WithGrey(this Color color, float value)  {            return new Color(color.r * value, color.g * value, color.b * value);  }  public static Color WithAlpha(this Color color, float alpha)  {            color.a = alpha;  return color;  }    }}  
#endif

参考

十分钟入门Unity革命性编辑器拓展插件——Odin_哔哩哔哩_bilibili
Odin常用功能整理 | 登峰造极者,殊途亦同归。 (lfzxb.top)
marijnz/unity-toolbar-extender at dbd76ed996483d219c39d240f785b1790aa039ed (github.com)

相关文章:

Unity编辑器拓展-Odin

1.相比于原生Unity的优势 Unity不支持泛型类型序列化&#xff0c;例如字典原生Unity不支持序列化&#xff0c;而Odin可以继承序列化的Mono实现功能强大且使用简单&#xff0c;原生Unity想实现一些常见的功能需要额外自己编写Unity扩展的编码&#xff0c;实现功能只需要加一个特…...

小红书婴童产业探索,解析消费者需求!

在消费升级、市场引导的背景下&#xff0c;众多产业都在悄然发生着变化&#xff0c;其中“婴童产业”就是非常有代表性的一个。今天就来深入分析小红书婴童产业探索&#xff0c;解析消费者需求&#xff01; 一、何为婴童产业 事实上&#xff0c;婴童产业&#xff0c;并不仅仅局…...

离线安装mysql客户端

下载路径 oracle网站总是在不断更新&#xff0c;所以下载位置随时可能变动但万变不离其宗&#xff0c;学习也要学会一通百通。 首先直接搜索&#xff0c;就能找找到mysql官网 打开网站&#xff0c;并点击 DOWNLOADS 往下滚动&#xff0c;找到社区版下载按钮。…...

Docker 数据管理

管理 Docker 容器中数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09; 数据卷容器&#xff08;DataVolumes Containers&#xff09;。 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xf…...

数据统计--图形报表--ApacheEcharts技术 --苍穹外卖day10

Apache Echarts 营业额统计 重点:已完成订单金额要排除其他状态的金额 根据时间选择区间 设计vo用于后端向前端传输数据,dto用于后端接收前端发送的数据 GetMapping("/turnoverStatistics")ApiOperation("营业额统计")public Result<TurnoverReportVO…...

【kubernetes的三种网络】

kubernetes的三种网络 一、三种网络service网络&#xff08;service是虚拟IP地址&#xff09;pod网络&#xff08;pod的IP地址 docker容器的IP&#xff09;节点网络&#xff08;网络服务器上的物理网卡IP&#xff09; 二、其他网络flannel一、vxlan(隧道方案)1.定义2.优势3.工作…...

Java中树形菜单的实现方式(超全详解!)

前言 这篇文中&#xff0c;我一共会用两种方式来实现目录树的数据结构&#xff0c;两种写法逻辑是一样的&#xff0c;只是一种适合新手理解&#xff0c;一种看着简单明了但是对于小白不是很好理解。在这里我会很详细的讲解每一步代码&#xff0c;主要是方便新人看懂&#xff0…...

基于Uniswap V3的去中心化前端现货交易平台Oku正式登陆Moonbeam

波卡上的Uniswap v3合约由Moonbeam智能合约、Oku前端&#xff0c;以及Wormhole远程路由技术共同实现。 跨链互连应用的最佳去中心化开发平台Moonbeam宣布Uniswap现已正式登陆。此次是Uniswap产品作为一个主流的DEX首次涉足Polkadot生态。用户可以通过新的、易于使用的Oku界面与…...

leetcode 每日一题复盘(10.9~10.15)

leetcode 101 对称二叉树 这道题一开始想是用层序遍历,看每一层是否都对称,遇到一个问题就是空指针(子树为空)无法记录下来,同时会导致操作空指针的问题,因此需要修改入队条件,并用一个标志去表示空指针 vector<int>numv;for(int i0;i<size;i){TreeNode*frontque.fro…...

【云计算网络安全】DDoS 缓解解析:DDoS 攻击缓解策略、选择最佳提供商和关键考虑因素

文章目录 一、前言二、什么是 DDoS 缓解三、DDoS 缓解阶段四、如何选择 DDoS 缓解提供商4.1 网络容量4.2 处理能力4.3 可扩展性4.4 灵活性4.5 可靠性4.6 其他考虑因素4.6.1 定价4.6.2 所专注的方向 文末送书《数据要素安全流通》本书编撰背景本书亮点本书主要内容 一、前言 云…...

如何巧用AI智能技术,让文物不再“无人问津”?

文物是文化与传统的象征&#xff0c;而博物馆则是展现文物的载体。传统的博物馆监控体系只是利用摄像头进行监控&#xff0c;无法将人工智能融入其中&#xff0c;使其更加智能化、信息化。那么&#xff0c;如何将AI技术与传统视频监控相融合呢&#xff1f;TSINGSEE青犀智能分析…...

一天一八股——SSL/TLS协议

早期设计的http协议存在诸多的问题&#xff0c;SSL/TLS在http的基础上保证了数据的保密&#xff0c;验证和身份验证 https的保密性通过混合加密的方式保证&#xff0c;解决窃听问题https数据的完整性通过摘要算法保证&#xff0c;通过数字证书CA的方式进行数据来源和数据可靠性…...

SpringCloud学习笔记-Eureka服务的搭建

目录 1.首先引入依赖2.main中配置注解3.src/main/resources/application.yml配置文件 本文的主要工作是介绍如何搭建一个Eureka服务 1.首先引入依赖 pom文件中加入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring…...

css如何实现页面布局与五种实现方式

CSS布局实现的主要方式有以下几种&#xff1a; 一、盒模型布局&#xff1a;CSS中&#xff0c;每个元素都是一个盒子&#xff0c;包括内容、内边距、边框和外边距。通过设置盒子的属性&#xff08;如宽度、高度、内边距、边框、定位等&#xff09;&#xff0c;可以实现不同的布…...

cv2.split函数与cv2.merge函数

split函数用于图像BGR通道的分离 merge函数用于可将分开的图像通道合并到一起 1.split函数的使用 这是原图&#xff0c;我们使用split函数对其三个通道进行分离。 注意&#xff1a;split函数分离通道的顺序是B、G、R。 以下方法是将三个通道的值都设置为与某一个通道相同。…...

Vue--1.7watch侦听器(监视器)

作用&#xff1a;监视数据变化&#xff0c;执行一些业务逻辑或异步操作。 语法&#xff1a; 1.简单写法->简单类型数据&#xff0c;直接监视 const app new Vue({el: #app,data: {words:},watch:{words(newValue,oldValue){}}}) const app new Vue({el: #app,data: {obj…...

序列:全序关系

一个序列满足全序关系必须满足以下条件&#xff1a; 反对称性&#xff1a;若 a ≤ b a\le b a≤b&#xff0c;则 b ≥ a b\ge a b≥a传递性&#xff1a;若 a ≤ b a\le b a≤b 且 b ≤ c b\le c b≤c&#xff0c;则 a ≤ c a\le c a≤c完全性&#xff1a; a ≤ b a\le b …...

100M服务器能同时容纳多少人访问?

100M的服务器带宽能够同时容纳的用户访问量需要考虑以下几个关键因素: &#x1f449;1.单个用户的平均访问流量大小 这取决于网站内容,是否有多媒体等。一般文本类网站每用户每次访问在50-100KB。 &#x1f449;2.每个用户的平均访问页面 通常每次访问会打开多个页面 &…...

Javascript 笔记:函数调用与函数上下文

在 JavaScript 中&#xff0c;函数上下文通常指的是函数在执行时的当前对象的引用&#xff0c;这通常用 this 关键字表示。this 关键字在不同的执行上下文中可能引用到不同的对象。 1 全局上下文 当 this 关键字用在全局上下文&#xff08;不在任何函数内部&#xff09;&#…...

【WebService】C#搭建的标准WebService接口,在使ESB模版作为参数无法获取参数数据

一、问题说明 1.1 问题描述 使用C# 搭建WebService接口&#xff0c;并按照ESB平台人员的要求&#xff0c;将命名空间改为"http://esb.webservice",使用PostmanESB平台人员提供的入参示例进行测试时&#xff0c;callBussiness接口参数message始终为null。 以下是ES…...

Sqlserver关于tempdb临时数据库文件个数的最佳实践

官方文档 https://learn.microsoft.com/zh-cn/sql/relational-databases/databases/tempdb-database?viewsql-server-ver16 https://learn.microsoft.com/en-US/troubleshoot/sql/database-engine/performance/recommendations-reduce-allocation-contention 一般而言&#x…...

【Java】微服务——微服务介绍和Eureka注册中心

目录 1.微服务介绍2.服务拆分和远程调用2.1.提供者与消费者 3.Eureka注册中心3.1.Eureka的结构和作用3.2.Eureka的结构3.3.搭建Eureka服务3.3.1.引入eureka依赖3.3.2.编写配置文件 3.4.服务注册及拉1&#xff09;引入依赖2&#xff09;配置文件3&#xff09;启动多个user-servi…...

C++ virtual 虚函数 虚基类

https://blog.csdn.net/xbb123456rt/article/details/81986691 基类指针可以指向一个派生类对象&#xff0c;但派生类指针不能指向基类对象。 可以在基类中将被重写的成员函数设置为虚函数&#xff0c;其含义是&#xff1a;当通过基类的指针或者引用调用该成员函数时&#xf…...

redis分布式秒杀锁

-- 获取锁标识&#xff0c;是否与当前线程一致&#xff1f; if(redis.call(get, KEYS[1]) ARGV[1]) then-- 一致&#xff0c;删除return redis.call(del, KEYS[1]) end -- 不一致&#xff0c;直接返回 return 0package com.platform.lock;public interface ILock {/*** 获取锁…...

【Redis】String内部编码方式

String内部编码方式 int: 8个字节的长整型embstr: 小于等于39个字节的字符串raw: 大于39个字节的字符串...

川西旅游网系统-前后端分离(前台vue 后台element UI,后端servlet)

前台&#xff1a;tour_forword: 川西旅游网前端----前台 (gitee.com) 后台&#xff1a;tour_back: 川西旅游网-------后台 (gitee.com) 后端 &#xff1a;tour: 川西旅游网------后端 (gitee.com)...

Paddle使用pyinstaller打包出错的解决方法

使用PyQt5开发了一个基于paddle ocr的文字识别程序&#xff0c;打包的时候报错了。给大家分享一下解决方法https://juejin.cn/post/7287562282669883451 为了测试paddle模块的打包问题&#xff0c;所以当前demo.py只引用了paddleOCR模块demo.py from paddleocr import PaddleO…...

【Java acm】特殊输入

input: [[1,2,3 ], [4, 5,6], [7,8]] output: [[1, 2, 3], [4, 5, 6], [7, 8]] 思路 按行读入, 然后进行字符串处理, 将其他字符替换为空字符.在split(,) repalceAll(“\s”,“”), 将所有空白字符替换成空字符(包括空格, 制表, 换行等) 代码实现 import java.util.*;publ…...

在Ubuntu 20.04搭建最小实验环境

sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates安装导入GPG公钥所需的依赖包。 sudo wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -导入GPG密钥。 sudo apt-get -y install --no-install-recommends software-p…...

使用uwsgi部署Flask

安装 这里直接使用包管理器提供的版本&#xff0c;不过建议大家使用pip来安装&#xff0c;会少一些坑&#xff1a; (Debian/Ubuntu) apt-get install uwsgi uwsgi-plugin-python3 或使用pip安装&#xff1a; pip3 install uwsgi 试试看 [demo.py] from flask import Flas…...

2017做那个网站致富/b2b十大平台排名

科技美学|大家测 活动(第191127期)由科技美学与 飞宇科技联合发起&#xff0c;邀你一起来试用最新的北通J1手游按键2只“ 参加本活动您需要付费 0元 ”报名地址&#xff1a;https://www.wenjuan.com/s/YnaeIzH/本期测评产品亮点介绍&#xff1a;北通J1手游按键&#xff0c;最新…...

张家港网站建设做网站/seo关键词排名优化专业公司

当整个世界都在为互联网喝彩的时候&#xff0c;人们心中往往都会进行这样的思考--我怎样才能在互联网上获得财富&#xff1f;其实&#xff0c;这个问题是没有人能够回答的&#xff0c;因为可以回答的人正在为获得财富忙得不可开交。 有人说&#xff1a;对网络经济来讲&#xff…...

网站建设方案预算/seo工作内容

case和select结构在技术上说并不是循环, 因为它们并不对可执行代码块进行迭代. 但是和循环相似的是, 它们也依靠在代码块顶部或底部的条件判断来决定程序的分支. 在代码块中控制程序分支 case (in) / esac 在shell中的case结构与C/C中的switch结构是相同的. 它允许通…...

淮北网站建设公司/独立站seo是什么意思

3月16日下午&#xff0c;庐江县2021年高考备考工作推进会在庐江中学行政楼三楼会议室如期顺利举行。县教体局党委书记、局长徐晓兵&#xff0c;县教体局党委委员、副局长孙溥&#xff0c;县教体局教研室主任傅求宝等出席会议&#xff0c;各高中学校校长、分管教学副校长及各校毕…...

云平台建设网站/热门搜索

沛纳海为 ORACLE TEAM USA 设计的三款全新特别版腕表具备多种特质︰以性能超卓的物料结合崭新科技&#xff1b;拥有先进功能之余操作简便&#xff0c;加上富传奇性的历史&#xff0c;与卫冕第35届美洲杯帆船赛的 ORACLE TEAM USA 的精神不谋而合。ORACLE TEAM USA 由航海传奇 J…...

自己建设网站怎么做/全网营销的公司

WPF-DataGrid(数据表格)美化 原文:WPF-DataGrid(数据表格)美化我们不多哔哔先上图&#xff1a; 数据表格使用背景&#xff1a; 当我们在做二次开发发现我我们的表格无法向WEB的表格一样好看&#xff0c;这时我们就需要对数据表格进行美化和重构 表格美化思维引导&#xff1a; W…...