当前位置:网站首页>Colorbar of using vertexehelper to customize controls (II)
Colorbar of using vertexehelper to customize controls (II)
2022-07-07 09:22:00 【heater404】
One 、 Realization ColorBar
What is? ColorBar??? Go straight to the renderings :
A set of data is mapped to a set of colors , Similar to pseudo color map . First we need to prepare a set of colors , We can choose HSV Color model ,HSV The color model is not explained in detail here , Students who don't know can learn by themselves .
So how to fill the vertex with color ? We can use rectangular vertices , The two vertices on the rectangle use the same color , The two vertices under the rectangle use the same color , The two vertices below this rectangle will coincide with the two vertices above the next rectangle .
/// <summary>
///
/// </summary>
/// <param name="h">0-1</param>
/// <param name="s">0-1</param>
/// <param name="v">0-1</param>
/// <returns></returns>
private Color HSVToRGB(float h)
{
return Color.HSVToRGB(h, 1, 1);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = this.GetPixelAdjustedRect();
vh.Clear();
var count = 15;// Define the number of rectangles , The more the number, the finer the color , But the greater the computational power . The corresponding color value will +1.
var posStep = (rect.yMax - rect.yMin) / count;
var colorStep = (1f - 0f) / count;// stay 0-1 Intermediate interpolation count-1 individual It has a total of count+1 A color is worth
// Of course , You may not need h stay 0-1 The scope of the You can modify the maximum and minimum values by yourself .
for (int i = 0; i < count; i++)
{
UIVertex[] verts = new UIVertex[4];
verts[0].position = new Vector3(rect.xMin, rect.yMin + i * posStep);
verts[0].color = HSVToRGB(i * colorStep);
verts[0].uv0 = Vector2.zero;
verts[1].position = new Vector3(rect.xMax, rect.yMin + i * posStep);
verts[1].color = HSVToRGB(i * colorStep);
verts[1].uv0 = Vector2.zero;
verts[2].position = new Vector3(rect.xMax, rect.yMin + (i + 1) * posStep);
verts[2].color = HSVToRGB((i + 1) * colorStep);
verts[2].uv0 = Vector2.zero;
verts[3].position = new Vector3(rect.xMin, rect.yMin + (i + 1) * posStep);
verts[3].color = HSVToRGB((i + 1) * colorStep);
verts[3].uv0 = Vector2.zero;
vh.AddUIVertexQuad(verts);
}
}

Next, we will add a graduation mark to the color . In order to reserve a position on the left side for the graduation mark , We cannot set the vertex position to xMin, So we changed it to this :
public class CreateRectangle : Graphic
{
int count = 15;// Define the number of rectangles , The more the number, the finer the color , But the greater the computational power . The corresponding color value will +1.
float hMin = 0;
float hMax = 1;
float leftMargin = 60;
protected override void Start()
{
base.Start();
StartCoroutine(MUpdateGeometry());
}
IEnumerator MUpdateGeometry()
{
while (true)
{
UpdateGeometry();
//SetVerticesDirty();
yield return new WaitForSeconds(2);
}
}
/// <summary>
///
/// </summary>
/// <param name="h">0-1</param>
/// <param name="s">0-1</param>
/// <param name="v">0-1</param>
/// <returns></returns>
private Color HSVToRGB(float h)
{
return Color.HSVToRGB(h, 1, 1);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = this.GetPixelAdjustedRect();
vh.Clear();
var posStep = (rect.yMax - rect.yMin) / count;
var colorStep = (hMax - hMin) / count;// stay 0-1 Intermediate interpolation count-1 individual It has a total of count+1 A color is worth
// Of course , You may not need h stay 0-1 The scope of the You can modify the maximum and minimum values by yourself .
for (int i = 0; i < count; i++)
{
UIVertex[] verts = new UIVertex[4];
verts[0].position = new Vector3(rect.xMin + leftMargin, rect.yMin + i * posStep);
verts[0].color = HSVToRGB(i * colorStep);
verts[0].uv0 = Vector2.zero;
verts[1].position = new Vector3(rect.xMax, rect.yMin + i * posStep);
verts[1].color = HSVToRGB(i * colorStep);
verts[1].uv0 = Vector2.zero;
verts[2].position = new Vector3(rect.xMax, rect.yMin + (i + 1) * posStep);
verts[2].color = HSVToRGB((i + 1) * colorStep);
verts[2].uv0 = Vector2.zero;
verts[3].position = new Vector3(rect.xMin + leftMargin, rect.yMin + (i + 1) * posStep);
verts[3].color = HSVToRGB((i + 1) * colorStep);
verts[3].uv0 = Vector2.zero;
vh.AddUIVertexQuad(verts);
}
}
}

We will use a tick mark Text On the left side , So we created a preform :

This prefabricated body Width We will set the width of the reserved position , The height will not change , then PosX It can be calculated to be fixed , Calculated according to the actual position PosY that will do .
public class CreateRectangle : Graphic
{
int count = 15;// Define the number of rectangles , The more the number, the finer the color , But the greater the computational power . The corresponding color value will +1.
float hMin = 0;
float hMax = 1;
float leftMargin = 60;
float markMax = 6000;
float markMin = 0;
GameObject markTextPrefab;
protected override void Start()
{
base.Start();
markTextPrefab = InitMarkTextPrefab();
StartCoroutine(MUpdateGeometry());
}
IEnumerator MUpdateGeometry()
{
while (true)
{
UpdateGeometry();
//SetVerticesDirty();
yield return new WaitForSeconds(2);
}
}
private GameObject InitMarkTextPrefab()
{
var prefab = (GameObject)Resources.Load("MarkText", typeof(GameObject));
var x = this.gameObject.GetComponent<RectTransform>().rect.width / 2 - leftMargin / 2;
prefab.GetComponent<RectTransform>().rect.Set(-x, 0, leftMargin, 20);
return prefab;
}
private void CreateOneMarkText(float yPos, string msg, string name)
{
var markText = (GameObject)Instantiate(markTextPrefab);
markText.name = "MarkText" + name;
markText.transform.SetParent(this.transform);
var originRect = markText.GetComponent<RectTransform>().rect;
markText.GetComponent<RectTransform>().rect.Set(originRect.x, yPos, originRect.width, originRect.height);
}
/// <summary>
///
/// </summary>
/// <param name="h">0-1</param>
/// <param name="s">0-1</param>
/// <param name="v">0-1</param>
/// <returns></returns>
private Color HSVToRGB(float h)
{
return Color.HSVToRGB(h, 1, 1);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = this.GetPixelAdjustedRect();
vh.Clear();
var posStep = (rect.yMax - rect.yMin) / count;
var colorStep = (hMax - hMin) / count;// stay 0-1 Intermediate interpolation count-1 individual It has a total of count+1 A color is worth
// Of course , You may not need h stay 0-1 The scope of the You can modify the maximum and minimum values by yourself .
var markStep = (markMax - markMin) / count;
for (int i = 0; i < count; i++)
{
UIVertex[] verts = new UIVertex[4];
var y0 = rect.yMin + i * posStep;
var h0 = i * colorStep;
verts[0].position = new Vector3(rect.xMin + leftMargin, y0);
verts[0].color = HSVToRGB(h0);
verts[0].uv0 = Vector2.zero;
verts[1].position = new Vector3(rect.xMax, y0);
verts[1].color = HSVToRGB(h0);
verts[1].uv0 = Vector2.zero;
CreateOneMarkText(y0, (i * markStep).ToString(), i.ToString());
var y1 = y0 + posStep;
var h1 = h0 + colorStep;
verts[2].position = new Vector3(rect.xMax, y1);
verts[2].color = HSVToRGB(h1);
verts[2].uv0 = Vector2.zero;
verts[3].position = new Vector3(rect.xMin + leftMargin, y1);
verts[3].color = HSVToRGB(h1);
verts[3].uv0 = Vector2.zero;
CreateOneMarkText(y1, ((i + 1) * markStep).ToString(), (i + 1).ToString());
vh.AddUIVertexQuad(verts);
}
}
}
however , Something is wrong. !!!
Trying to add MarkText15 (UnityEngine.UI.Text) for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.
This is because : Instantiate a UI Control called graphic rebuild operation , and OnPopulateMesh(), The function is in graphic rebuild Called in the operation , So if in OnPopulateMesh(), Create a new UI Control, the system will prompt an error :graphic rebuild The operation is called circularly , So we need to use CO process operation (IEnumerator), After entering the process operation , Must be carried out immediately yield return new WaitForSeconds(0), Temporarily exit the current collaboration , Give Way graphic rebuild The operation is performed first .
So we need to put CreateOneMarkText It is modified as collaborative process :
IEnumerator CreateOneMarkText(float yPos, string msg, string name)
{
yield return new WaitForSeconds(0);
name = "MarkText" + name;
var markText = this.transform.Find(name)?.gameObject;
if (markText == null)
{
markText = (GameObject)Instantiate(markTextPrefab);
markText.name = name;
}
markText.transform.SetParent(this.transform);
var x = this.gameObject.GetComponent<RectTransform>().rect.width / 2 - leftMargin / 2;
markText.GetComponent<RectTransform>().anchoredPosition = new Vector2(-x, yPos);
markText.GetComponent<Text>().text = msg + "-";
}
Then change the place called to :
StartCoroutine(CreateOneMarkText(y0, (i * markStep).ToString(), i.ToString()));

Two 、 Adding unit
After the above is completed, you will find a problem , The scale information at the bottom and top exceeds the size of the control , So we also need to leave a margin , Then we need to consider the location of the unit . We still need to give ColorBar Add a background , The complete code is as follows :
public class ColorBar : Graphic
{
public Color MarkTextColor;
public int UIVertexQuadCount = 15;// Define the number of rectangles , The more the number, the finer the color , But the greater the computational power . The corresponding color value will +1.
float hMin = 0;
float hMax = 1;
public float MarkTextWidth = 40;
public float MarkTextHeight = 16;
float markMax = 6000;
float markMin = 0;
GameObject markTextPrefab;
protected override void Start()
{
base.Start();
markTextPrefab = InitMarkTextPrefab();
}
IEnumerator MUpdateGeometry()
{
while (true)
{
UpdateGeometry();
//SetVerticesDirty();
yield return new WaitForSeconds(2);
}
}
private GameObject InitMarkTextPrefab()
{
var game = (GameObject)Resources.Load("MarkText", typeof(GameObject));
return game;
}
IEnumerator CreateOneMarkText(float yPos, string msg, string name)
{
yield return new WaitForSeconds(0);
name = "MarkText" + name;
var markText = this.transform.Find(name)?.gameObject;
if (markText == null)
{
markText = (GameObject)Instantiate(markTextPrefab);
markText.name = name;
}
markText.transform.SetParent(this.transform);
var x = this.gameObject.GetComponent<RectTransform>().rect.width / 2 - MarkTextWidth / 2;
markText.GetComponent<RectTransform>().sizeDelta = new Vector2(MarkTextWidth, MarkTextHeight);
markText.GetComponent<RectTransform>().anchoredPosition = new Vector2(-x, yPos);
markText.GetComponent<Text>().text = msg;
markText.GetComponent<Text>().color = MarkTextColor;
}
private Color CreateOneColor(float h)
{
return Color.HSVToRGB(h, 1, 1);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = this.GetPixelAdjustedRect();
vh.Clear();
float yMax, yMin;
yMax = rect.yMax - MarkTextHeight / 2;
// Because the company information needs to be reserved at the bottom , So add an extra space .
yMin = rect.yMin + MarkTextHeight / 2 + MarkTextHeight / 2;
var posStep = (yMax - yMin) / UIVertexQuadCount;
var colorStep = (hMax - hMin) / UIVertexQuadCount;// Intermediate interpolation count-1 individual It has a total of count+1 A color is worth
var markStep = (markMax - markMin) / UIVertexQuadCount;
// Adding unit
StartCoroutine(CreateOneMarkText(yMin - MarkTextHeight / 2, "mm", "unit"));
for (int i = 0; i < UIVertexQuadCount; i++)
{
UIVertex[] verts = new UIVertex[4];
var y0 = yMin + i * posStep;
var h0 = hMin + i * colorStep;
verts[0] = CreateOneUIVertex(new Vector2(rect.xMin + MarkTextWidth, y0), h0);
verts[1] = CreateOneUIVertex(new Vector2(rect.xMax, y0), h0);
var m0 = markMin + (i * markStep);
StartCoroutine(CreateOneMarkText(y0, m0.ToString() + "-", i.ToString()));
var y1 = y0 + posStep;
var h1 = h0 + colorStep;
verts[2] = CreateOneUIVertex(new Vector2(rect.xMax, y1), h1);
verts[3] = CreateOneUIVertex(new Vector2(rect.xMin + MarkTextWidth, y1), h1);
var m1 = m0 + markStep;
StartCoroutine(CreateOneMarkText(y1, m1.ToString() + "-", (i + 1).ToString()));
vh.AddUIVertexQuad(verts);
}
}
private UIVertex CreateOneUIVertex(Vector2 pos, float h)
{
UIVertex vert = new UIVertex();
vert.position = new Vector3(pos.x, pos.y);
vert.color = CreateOneColor(h);
vert.uv0 = Vector2.zero;
return vert;
}
}

3、 ... and 、 reflection
After adding units above, it seems to be over ??? No , In fact, we made a mistake above , That is, we drew a color band first , Then add a tick mark , How can it be wrong here ? Because the number of tick marks is not fixed , When the size of the control changes, the scale line will also need to change accordingly . therefore , Let's rearrange :
- We first have a mapping relationship between value and color
- We tile the value range linearly on the interface ( Axis ), The creation process of coordinate axis is roughly : Calculate the number of scales according to the height of a coordinate scale and the height of the coordinate axis , Then calculate the scale step with the coordinate axis range .
- After creating the coordinate axis, draw a color band according to the mapping relationship between value and color ( How many colors to draw depends on the number of scales )
therefore , Our code has changed a lot :
public class ColorBar : Graphic
{
public int UIVertexQuadNumBetweenTwoMarkText = 3;
const string MarkTextNameBase = "MarkText";
public string unit = "unknown";
readonly float[] MarkTabs = new float[] {
1, 2, 5 };
public Color MarkTextColor = Color.white;
public float MarkTextWidth = 40;
public float MarkTextHeight = 16;
private float markTextMax = 6200;
public Func<float, float, float, Color> CalcOneColor;
public float MarkTextMax
{
get {
return markTextMax; }
set
{
if (value > markTextMin)
{
markTextMax = value;
UpdateGeometry();// call OnPopulateMesh
}
}
}
private float markTextMin = 100;
public float MarkTextMin
{
get {
return markTextMin; }
set
{
if (value < markTextMax)
{
markTextMin = value;
UpdateGeometry();// call OnPopulateMesh
}
}
}
GameObject markTextPrefab;
protected override void Start()
{
base.Start();
markTextPrefab = InitMarkTextPrefab();
}
private GameObject InitMarkTextPrefab()
{
var game = (GameObject)Resources.Load("MarkText", typeof(GameObject));
return game;
}
void CreateOneMarkText(float yPos, string msg)
{
string name = MarkTextNameBase + msg;
var markText = this.transform.Find(name)?.gameObject;
if (markText == null)
{
markText = (GameObject)Instantiate(markTextPrefab);
markText.name = name;
}
markText.transform.SetParent(this.transform);
var x = this.gameObject.GetComponent<RectTransform>().rect.width / 2 - MarkTextWidth / 2;
var rectTransform = markText.GetComponent<RectTransform>();
rectTransform.sizeDelta = new Vector2(MarkTextWidth, MarkTextHeight);
rectTransform.anchoredPosition = new Vector2(-x, yPos);
var text = markText.GetComponent<Text>();
text.text = msg;
text.color = MarkTextColor;
text.enabled = true;
}
IEnumerator CreateMarkTextUnit(float yPos, string msg)
{
yield return new WaitForSeconds(0);
CreateOneMarkText(yPos, msg);
}
IEnumerator CreateMarkText(float markTextYPos, float markText)
{
yield return new WaitForSeconds(0);
CreateOneMarkText(markTextYPos, markText.ToString() + "-");
}
IEnumerator DisableAllMarkTextWithoutUnit()
{
yield return new WaitForSeconds(0);
var children = this.GetComponentsInChildren<Text>();
foreach (var child in children)
{
if (child.name != MarkTextNameBase + unit)
child.enabled = false;
}
}
IEnumerator DestoryAllDisabledMarkText()
{
yield return new WaitForSeconds(0);
var children = this.GetComponentsInChildren<Text>();
foreach (var child in children)
{
if (!child.enabled)
DestroyImmediate(child.gameObject);
}
}
private float CalcRealStartMarkText(float markText)
{
int basePrecision = 1;
while (markText < basePrecision || markText > basePrecision * 10)
{
if (markText < basePrecision)
basePrecision /= 10;
else if (markText > basePrecision * 10)
basePrecision *= 10;
else
break;
}
basePrecision /= 10;
return Mathf.CeilToInt(markText / basePrecision) * basePrecision;
}
protected override void OnPopulateMesh(VertexHelper vh)
{
var rect = this.GetPixelAdjustedRect();
vh.Clear();
//ColorBar Coordinate position of rectangle ( Pixel unit )
float yMax, yMin, xMin, xMax;
yMax = rect.yMax - MarkTextHeight / 2;
// Because the company information needs to be reserved at the bottom , So add an extra space .
yMin = rect.yMin + MarkTextHeight / 2 + MarkTextHeight / 2;
xMin = rect.xMin + MarkTextWidth;
xMax = rect.xMax;
var markTextNum = CalcMarkTextNum(MarkTextHeight, yMax - yMin);
float markTextStep = CalcMarkTextStep(MarkTabs, markTextNum, markTextMax - markTextMin);
var rate = (yMax - yMin) / (markTextMax - markTextMin);
var realMarkTextMin = CalcRealStartMarkText(markTextMin);
// Adding unit
StartCoroutine(CreateMarkTextUnit(yMin - MarkTextHeight / 2, unit));
StartCoroutine(DisableAllMarkTextWithoutUnit());
var markText = realMarkTextMin;
while (markText <= markTextMax)
{
// Current tick mark
var markTextYPos = (markText - markTextMin) * rate + yMin;
StartCoroutine(CreateMarkText(markTextYPos, markText));
// Due to the large span between the two graduation marks , Only draw a rectangle, and the color transition is not good , So here, draw several between the two tick marks
var uiVertexQuadStep = markTextStep / UIVertexQuadNumBetweenTwoMarkText;
for (int i = 0; i < UIVertexQuadNumBetweenTwoMarkText; i++)
{
// Previous tick mark
var markTextPre = markText - (i + 1) * uiVertexQuadStep;
if (markTextPre < markTextMin)
markTextPre = markTextMin;
var markTextYPosPre = (markTextPre - markTextMin) * rate + yMin;
var verts = CreateUIVertexQuad(xMin, xMax, markTextYPosPre, markTextYPos, markTextPre, markText);
vh.AddUIVertexQuad(verts);
if (markTextPre < markTextMin)
break;
}
// Next tick mark
markText += markTextStep;
}
// Jumping here means that the scale line is greater than the maximum value , Those beyond the maximum scale line certainly do not need to be drawn
// The maximum scale line does not need to be drawn , It may not be the data accuracy we want
// But the color value corresponding to the maximum scale line needs to be drawn , It is equivalent to the minimum scale color value also needs to be drawn
var lastmarkText = markText - markTextStep;// This is the largest scale line drawn
var lastmarkTextYPos = (lastmarkText - markTextMin) * rate + yMin;
var lastverts = CreateUIVertexQuad(xMin, xMax, lastmarkTextYPos, yMax, lastmarkText, markTextMax);
vh.AddUIVertexQuad(lastverts);
StartCoroutine(DestoryAllDisabledMarkText());
}
private UIVertex[] CreateUIVertexQuad(float xMin, float xMax,
float yBottomPos, float yTopPos, float markBottom, float markTop)
{
UIVertex[] verts = new UIVertex[4];
verts[0] = CreateOneUIVertex(new Vector2(xMin, yBottomPos), markBottom);
verts[1] = CreateOneUIVertex(new Vector2(xMax, yBottomPos), markBottom);
verts[2] = CreateOneUIVertex(new Vector2(xMax, yTopPos), markTop);
verts[3] = CreateOneUIVertex(new Vector2(xMin, yTopPos), markTop);
return verts;
}
private UIVertex CreateOneUIVertex(Vector2 pos, float h)
{
UIVertex vert = new UIVertex();
vert.position = new Vector3(pos.x, pos.y);
if (CalcOneColor != null)
vert.color = CalcOneColor.Invoke(h, markTextMin, markTextMax);
vert.uv0 = Vector2.zero;
return vert;
}
/// <summary>
/// Calculate the number of tick marks , multiply 3 Because the interval between the scale marks is given
/// </summary>
/// <param name="markTextHeigh"> The height occupied by a scale line </param>
/// <param name="colorBarHeigh"> Length of the entire shaft </param>
/// <returns></returns>
private int CalcMarkTextNum(float markTextHeigh, float colorBarHeigh)
{
return Mathf.FloorToInt(colorBarHeigh / (markTextHeigh * 3));
}
/// <summary>
/// Find the best scale step
/// </summary>
/// <param name="markTab"> Provide an alternative step size base </param>
/// <param name="markNum"> Number of scales </param>
/// <param name="MarkTextRange"> The whole range that needs to be crossed </param>
/// <returns> Optimal step size </returns>
private float CalcMarkTextStep(float[] markTab, int markNum, float MarkTextRange)
{
float step = MarkTextRange / (markNum > 0 ? markNum : 1);
float markScale = 1;
float mark = markTab[0];
while (step < mark * markScale || step > mark * markScale * 10)
{
if (step < mark * markScale)
markScale /= 10;
else if (step > mark * markScale * 10)
markScale *= 10;
else
break;
}
step /= markScale;
for (int i = 1; i < markTab.Length; i++)
{
if (Mathf.Abs(markTab[i] - step) < Mathf.Abs(mark - step))
mark = markTab[i];
}
return mark * markScale;
}
}
Considering that you want to make controls , So we wrote the mutable things in another user script :
public class CustomColorBar : MonoBehaviour
{
public ColorBar Bar;
public float Max
{
get {
return Bar.MarkTextMax; }
set {
Bar.MarkTextMax = value; }
}
public float Min
{
get {
return Bar.MarkTextMin; }
set {
Bar.MarkTextMin = value; }
}
// Use this for initialization
void Start()
{
Bar.CalcOneColor = CreateOneColor;
StartCoroutine(UpdateMaxAndMin());
}
IEnumerator UpdateMaxAndMin()
{
while (true)
{
Min= Random.Range(0, 3000);
yield return new WaitForSeconds(5);
Max = Random.Range(Min, 7000);
yield return new WaitForSeconds(5);
}
}
// Update is called once per frame
void Update()
{
}
private Color CreateOneColor(float value, float min, float max)
{
float hMin = 0, hMax = 0.80f;
var h = (hMax - hMin) / (max - min) * (value - min) + hMin;
return Color.HSVToRGB(h, 1, 1);
}
}

Four 、 Custom control

And then in Editor A script is added to the folder :
public class ColorBarMenu : MonoBehaviour
{
/// If you want to make Hierarchy Inside Gameobject Right click
/// This option appears in the pop-up dialog , You need to add this option to "GameObject" Under the table of contents
[MenuItem("GameObject/UI/ColorBar")]
public static void AddColorBarInGameObject()
{
GameObject parent = null;
if (null != Selection.activeTransform)
{
parent = Selection.activeTransform.gameObject;
}
else
{
parent = null;
}
if ((null == parent) || (null == parent.GetComponentInParent<Canvas>()))
{
Canvas canvas = FindObjectOfType<Canvas>();
if (null == canvas)
{
Debug.LogError("AddColorBar : can not find a canvas in scene!");
return;
}
else
{
parent = FindObjectOfType<Canvas>().gameObject;
}
}
GameObject prefab = Resources.Load("Prefabs/ColorBar") as GameObject;
if (null == prefab)
{
Debug.LogError("AddColorBar : Load ColorBar Error!");
return;
}
GameObject colorBar;
if (null != parent)
colorBar = Instantiate(prefab, parent.transform);
else
colorBar = Instantiate(prefab);
if (null == colorBar)
{
Debug.LogError("AddColorBar : Instantiate ColorBar Error!");
return;
}
Undo.RegisterCreatedObjectUndo(colorBar, "Created ColorBar");
colorBar.name = "ColorBar";
}
}
This script is to add this control directly by right clicking . All source code in :heater404/ColorBar (github.com)
There are two examples in the source code :

边栏推荐
- Detailed learning notes of JVM memory structure (I)
- 串口实验——简单数据收发
- Common short chain design methods
- Postman interface debugging method
- PMP experience learning and sharing process
- Difference between interface iterator and iteratable
- 网易云微信小程序
- How long does the PMP usually need to prepare for the exam in advance?
- 信息安全实验三 :PGP邮件加密软件的使用
- Interface test API case, data and interface separation
猜你喜欢

端口复用和重映像

Error: selenium common. exceptions. WebDriverException: Messag‘geckodriver‘ execute

Reflections on the way of enterprise IT architecture transformation (Alibaba's China Taiwan strategic thought and architecture practice)

信息安全实验二 :使用X-SCANNER扫描工具

The configuration and options of save actions are explained in detail, and you won't be confused after reading it

C language pointer (Part 2)

Mysql数据库-锁-学习笔记

C语言指针(特别篇)

四、机器学习基础

How to use Arthas to view class variable values
随机推荐
Analysis of Hessian serialization principle
华为HCIP-DATACOM-Core_03day
PMP Exam details after the release of the new exam outline
Interview question: general layout and wiring principles of high-speed PCB
How to count the number of project code lines
Full link voltage test of the e-commerce campaign Guide
DRF defines views and routes
Where is the answer? action config/Interceptor/class/servlet
Regularly modify the system time of the computer
C语言指针(下篇)
Pycharm create a new file and add author information
【SVN】SVN是什么?怎么使用?
Postman interface test (I. installation and use)
STM32的时钟系统
Data association between two interfaces of postman
PMP examination experience sharing
Mysql database transaction learning notes
PMP experience learning and sharing process
C语言指针(习题篇)
Detailed learning notes of JVM memory structure (I)