当前位置:网站首页>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 :
边栏推荐
- STM32串口寄存器库函数配置方法
- STM32 serial port register library function configuration method
- Systick滴答定时器
- PMP Exam details after the release of the new exam outline
- Self awakening from a 30-year-old female programmer
- Sublime Text4 download the view in bower and set the shortcut key
- Upgrade Alibaba cloud RDS (relational database service) instance to com mysql. jdbc. exceptions. Troubleshooting of jdbc4.communicationsexception
- PMP Exam Preparation experience, seek common ground while reserving differences, and successfully pass the exam
- Leetcode daily questions (2316. count unreachable pairs of nodes in an undirected graph)
- Entity of cesium data visualization (Part 1)
猜你喜欢
[istio introduction, architecture, components]
Integer or int? How to select data types for entity classes in ORM
Error: selenium common. exceptions. WebDriverException: Messag‘geckodriver‘ execute
Skill review of test engineer before interview
Implementation of corner badge of Youmeng message push
Why is access to the external network prohibited for internal services of the company?
STM32 serial port register library function configuration method
Screen automatically generates database documents
Register address name mapping
Common short chain design methods
随机推荐
[SVN] what is SVN? How do you use it?
串口實驗——簡單數據收發
Systick tick timer
Difference between interface iterator and iteratable
Skill review of test engineer before interview
信息安全实验三 :PGP邮件加密软件的使用
2020 year end summary
Error: selenium common. exceptions. WebDriverException: Messag‘geckodriver‘ execute
STM32 and motor development (from stand-alone version to Networking)
Port occupation troubleshooting
【云原生】DevOps(一):DevOps介绍及Code工具使用
E-commerce campaign Guide
Druid monitoring - Introduction to JMX usage and principle
PMP certificate preparation experience sharing
Simulation volume leetcode [general] 1557 The minimum number of points that can reach all points
NVIC中断优先级管理
超十万字_超详细SSM整合实践_手动实现权限管理
Using JWT to realize login function
Sublime Text4 download the view in bower and set the shortcut key
MySql数据库-索引-学习笔记