UE4/UE5 c++绘制编辑器场景直方图(源码包含场景中的像素获取、菜单添加ToolBar)

news/2025/2/12 23:36:08/

UE4/UE5 c++场景直方图

    • UE4/UE5 C++绘制编辑器场景直方图
    • 绘制原理:
      • 元素绘制
      • 坐标轴绘制
    • 源码处理

UE4/UE5 C++绘制编辑器场景直方图

注:源码包含场景中的像素获取、菜单添加ToolBar

实现效果
在这里插入图片描述

在这里插入图片描述

这个是用于美术统计场景中像素元素分布,类似于PS里面的直方图,比如,R值是255的像素,场景中的个数是1000个,那么就是(255,1000)。

绘制原理:

元素绘制

根据不同高度绘制了不同的Box
在这里插入图片描述

坐标轴绘制

箭头,一段段的标尺,坐标轴,均由绘制线构成

源码处理

首先创建一个Editor模块:
创建SCompoundWidget:
.h

// Fill out your copyright notice in the Description page of Project Settings.#pragma once
#include "CoreMinimal.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"/*** */
class SLASHEDITOR_API SPixHistogramWidget : public SCompoundWidget
{
public:SLATE_BEGIN_ARGS(SPixHistogramWidget){}SLATE_END_ARGS()virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect,FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle,bool bParentEnabled) const override;virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;void DrawPixelOnScreen(const TMap<int, float>& InData, const float InBarWidth,const float InBarGap,const FGeometry& InAllottedGeometry, FSlateWindowElementList& InOutDrawElements,const int32 InLayerId,const FVector2D InEdgeSize,const FLinearColor InColor) const;virtual bool SupportsKeyboardFocus() const override;void SelectionChanged(TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo);void GetScreenPixelValue();void RefreshData();void ResetMap();void OnScalingFactorChanged(float InScalingFactor);/** Constructs this widget with InArgs */void Construct(const FArguments& InArgs);private:  TArray<TSharedPtr<FString>> Pass;int CurrentIndex = 0;FVector2D LinearScale = FVector2D(3.f, 3.f);FVector2D BottomSize = FVector2D(40.f,40.f);float HeightScale = 1.f;const float HeightScalingFactor = 500;TArray<FColor> PixelsColor;TMap<int,float> GrayData;TMap<int,float> RData;TMap<int,float> GData;TMap<int,float> BData;bool IsCtrlDown = false;float ScalingFactor = 5.f;
};

.cpp

// Fill out your copyright notice in the Description page of Project Settings.#include "PixHistogramWidget.h"#include "Editor.h"
#include "SlateOptMacros.h"
#include "Widgets/Input/SComboBox.h"
#include "Widgets/Input/SSpinBox.h"
#include "Widgets/Input/STextComboBox.h"BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION#define LOCTEXT_NAMESPACE "PixHistogramWidget"void SPixHistogramWidget::Construct(const FArguments& InArgs)
{PixelsColor.Init(FColor::White, 65535);ResetMap();Pass.Empty();CurrentIndex = 0;Pass = {MakeShared<FString>("R"),MakeShared<FString>("G"),MakeShared<FString>("B"),MakeShared<FString>("Gray"),MakeShared<FString>("RGB"),MakeShared<FString>("RGB And Gray")};ChildSlot[SNew(SBox).WidthOverride(500).HeightOverride(200).HAlign(HAlign_Left).VAlign(VAlign_Bottom).Padding(FMargin(0, -200, 0, 0))[SNew(SHorizontalBox)+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Top)[SNew(STextComboBox).InitiallySelectedItem(Pass[CurrentIndex]).OptionsSource(&Pass).OnSelectionChanged(this, &SPixHistogramWidget::SelectionChanged)]+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center).Padding(20, 0, 0, 0)[SNew(STextBlock).Text(FText::FromString(TEXT("scale speed:")))]+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center)[SNew(SSpinBox<float>).Value(ScalingFactor).MinValue(0.0f).MaxValue(100.0f).OnValueChanged(this, &SPixHistogramWidget::OnScalingFactorChanged)]]];bCanSupportFocus = true;
}void SPixHistogramWidget::SelectionChanged(TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo)
{CurrentIndex = Pass.Find(NewSelection);
}void SPixHistogramWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);RefreshData();
}int32 SPixHistogramWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry,const FSlateRect& MyCullingRect,FSlateWindowElementList& OutDrawElements, int32 LayerId,const FWidgetStyle& InWidgetStyle,bool bParentEnabled) const
{const int32 NewLayerId = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect,OutDrawElements, LayerId, InWidgetStyle,bParentEnabled);const float BarWidth = 1.f * LinearScale.X;const float BarGap = 0.f * LinearScale.X;const FVector2D EdgeSize = FVector2D(0, 0);const FVector2D BackGroundSize = FVector2D((256 * LinearScale.X + EdgeSize.X * 2),(100 * LinearScale.Y + EdgeSize.Y * 2));if (CurrentIndex == 0){DrawPixelOnScreen(RData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Red);}else if (CurrentIndex == 1){DrawPixelOnScreen(GData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Green);}else if (CurrentIndex == 2){DrawPixelOnScreen(BData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Blue);}else if (CurrentIndex == 3){DrawPixelOnScreen(GData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Gray);}else if (CurrentIndex == 4){DrawPixelOnScreen(RData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Red);DrawPixelOnScreen(GData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Green);DrawPixelOnScreen(BData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Blue);}else if (CurrentIndex == 5){DrawPixelOnScreen(RData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Red);DrawPixelOnScreen(GData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Green);DrawPixelOnScreen(BData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Blue);DrawPixelOnScreen(GrayData, BarWidth, BarGap,AllottedGeometry, OutDrawElements,LayerId, EdgeSize, FLinearColor::Gray);}//Background frame drawingFSlateDrawElement::MakeBox(OutDrawElements,LayerId + 10,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y), BackGroundSize),new FSlateBrush(),ESlateDrawEffect::None,FLinearColor(0, 0, 0, 0.3));//X Axis rulerFLinearColor XColor = FLinearColor::White;FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(BottomSize.X + BackGroundSize.X + 20, AllottedGeometry.Size.Y - BottomSize.Y)},ESlateDrawEffect::None,XColor);FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X + BackGroundSize.X + 20, AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(BottomSize.X + BackGroundSize.X + 10, AllottedGeometry.Size.Y - BottomSize.Y + 5)},ESlateDrawEffect::None,XColor);FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X + BackGroundSize.X + 20, AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(BottomSize.X + BackGroundSize.X + 10, AllottedGeometry.Size.Y - BottomSize.Y - 5)},ESlateDrawEffect::None,XColor);//X Axis rulerint XDefaultSectionNum = FMath::Clamp<int>(8 * LinearScale.X, 2, 256);const FSlateFontInfo XCoordinateFont = FCoreStyle::GetDefaultFontStyle("Regular", 8);for (int i = 1; i <= XDefaultSectionNum; ++i){FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X + BackGroundSize.X / XDefaultSectionNum * i,AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(BottomSize.X + BackGroundSize.X / XDefaultSectionNum * i,AllottedGeometry.Size.Y - BottomSize.Y - 3)},ESlateDrawEffect::None,XColor);FSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X + BackGroundSize.X / XDefaultSectionNum * i,AllottedGeometry.Size.Y - BottomSize.Y + 5),FVector2D(30, 30)),FString::FromInt(256 / XDefaultSectionNum * i - 1),XCoordinateFont,ESlateDrawEffect::None,FLinearColor::White);}//Y AxisFLinearColor YColor = FLinearColor::White;FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y - 20)},ESlateDrawEffect::None,YColor);FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y - 20),FVector2D(BottomSize.X - 5, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y - 10)},ESlateDrawEffect::None,YColor);FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y - 20),FVector2D(BottomSize.X + 5, AllottedGeometry.Size.Y - BackGroundSize.Y - BottomSize.Y - 10)},ESlateDrawEffect::None,YColor);//Y Axis rulerint YDefaultSectionNum = FMath::Clamp<int>(5 * LinearScale.Y, 2, static_cast<int>(100 / HeightScale * HeightScalingFactor) - 1);const FSlateFontInfo YCoordinateFont = FCoreStyle::GetDefaultFontStyle("Regular", 8);for (int i = 1; i <= YDefaultSectionNum; ++i){FSlateDrawElement::MakeLines(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(),TArray<FVector2D>{FVector2D(BottomSize.X,AllottedGeometry.Size.Y - BackGroundSize.Y / YDefaultSectionNum * i - BottomSize.Y),FVector2D(BottomSize.X + 3,AllottedGeometry.Size.Y - BackGroundSize.Y / YDefaultSectionNum * i - BottomSize.Y)},ESlateDrawEffect::None,YColor);FSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X + 5,AllottedGeometry.Size.Y - BackGroundSize.Y / YDefaultSectionNum * i - BottomSize.Y),FVector2D(30, 30)),FString::FromInt(100 / HeightScale * HeightScalingFactor / YDefaultSectionNum * i),YCoordinateFont,ESlateDrawEffect::None,FLinearColor::White);}//Origin of coordinateconst FSlateFontInfo CoordinateFont = FCoreStyle::GetDefaultFontStyle("Regular", 10);FSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X - 15, AllottedGeometry.Size.Y - BottomSize.Y),FVector2D(30, 30)),FString(TEXT("O")),CoordinateFont,ESlateDrawEffect::None,FLinearColor::White);//X Axis labelingFSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X + BackGroundSize.X + 20, AllottedGeometry.Size.Y - BottomSize.Y - 15),FVector2D(30, 30)),FString(TEXT("Color Pass(0 ~ 255)")),CoordinateFont,ESlateDrawEffect::None,XColor);//Y Axis labelingFSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(BottomSize.X + 5, AllottedGeometry.Size.Y - BottomSize.Y - BackGroundSize.Y - 20),FVector2D(30, 30)),FString(TEXT("Pixel Num")),CoordinateFont,ESlateDrawEffect::None,YColor);//Operation reminderconst FSlateFontInfo TooltipsFontInfo = FCoreStyle::GetDefaultFontStyle("Regular", 15);FSlateDrawElement::MakeText(OutDrawElements,LayerId + 12,AllottedGeometry.ToPaintGeometry(FVector2D(AllottedGeometry.Size.X / 2 - 425, 0), FVector2D(30, 30)),FString(TEXT("Adjust the zoom with the mouse wheel. Press and hold Ctrl + mouse wheel to adjust the height.")),TooltipsFontInfo,ESlateDrawEffect::None,FLinearColor::White);return NewLayerId;
}FReply SPixHistogramWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{const float ScrollDelta = MouseEvent.GetWheelDelta();const float TempScalingFactor = 1.f + ScalingFactor * 0.01f;if (IsCtrlDown){if (ScrollDelta > 0){HeightScale = HeightScale * TempScalingFactor;}else if (ScrollDelta < 0){HeightScale = HeightScale / TempScalingFactor;}}else{if (ScrollDelta > 0){LinearScale = LinearScale * TempScalingFactor;}else if (ScrollDelta < 0){LinearScale = LinearScale / TempScalingFactor;}}return FReply::Handled();
}FReply SPixHistogramWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{IsCtrlDown = InKeyEvent.IsControlDown();return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent);
}FReply SPixHistogramWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{IsCtrlDown = false;return SCompoundWidget::OnKeyUp(MyGeometry, InKeyEvent);
}void SPixHistogramWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent);FSlateApplication::Get().SetKeyboardFocus(SharedThis(this), EFocusCause::OtherWidgetLostFocus);
}void SPixHistogramWidget::DrawPixelOnScreen(const TMap<int, float>& InData, const float InBarWidth,const float InBarGap,const FGeometry& InAllottedGeometry,FSlateWindowElementList& InOutDrawElements,const int32 InLayerId, const FVector2D InEdgeSize,const FLinearColor InColor) const
{for (int i = 0; i < InData.Num(); ++i){const float BarHeight = InData[i] * LinearScale.Y * HeightScale / HeightScalingFactor;FVector2D BarPosition(i * (InBarWidth + InBarGap) + InEdgeSize.X,InAllottedGeometry.Size.Y - BarHeight - InEdgeSize.Y);FVector2D BarSize(InBarWidth, BarHeight);FSlateDrawElement::MakeBox(InOutDrawElements,InLayerId,InAllottedGeometry.ToPaintGeometry(BarPosition + FVector2D(BottomSize.X, -BottomSize.Y), BarSize),new FSlateBrush(),ESlateDrawEffect::None,InColor);}
}bool SPixHistogramWidget::SupportsKeyboardFocus() const
{return true;
}void SPixHistogramWidget::GetScreenPixelValue()
{PixelsColor.Empty();FViewport* Viewport = GEditor->GetActiveViewport();Viewport->ReadPixels(PixelsColor);
}void SPixHistogramWidget::RefreshData()
{GetScreenPixelValue();ResetMap();//float Gray = 0.299 * Color.R + 0.587 * Color.G + 0.114 * Color.B;for (const FColor Color : PixelsColor){RData[Color.R] += 1;GData[Color.G] += 1;BData[Color.B] += 1;const int Index = FMath::Clamp<int>((0.299 * Color.R + 0.587 * Color.G + 0.114 * Color.B), 0, 255);GrayData[Index] += 1;}
}void SPixHistogramWidget::ResetMap()
{GrayData.Empty();RData.Empty();GData.Empty();BData.Empty();for (int i = 0; i <= 255; ++i){GrayData.Add(i, 0);RData.Add(i, 0);GData.Add(i, 0);BData.Add(i, 0);}
}void SPixHistogramWidget::OnScalingFactorChanged(float InScalingFactor)
{ScalingFactor = InScalingFactor;
}END_SLATE_FUNCTION_BUILD_OPTIMIZATION

Module添加内容:
.h

#pragma once#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"class FSlashEditorModule : public IModuleInterface
{
public:virtual void StartupModule() override;virtual void ShutdownModule() override;void AddProjectToolsMenu(FMenuBarBuilder& MenuBuilder);void FillProjectToolsMenu(FMenuBuilder& MenuBuilder);private:TSharedRef<SDockTab> SpawnPixHistogramTab(const FSpawnTabArgs& Args);static TSharedRef<FWorkspaceItem> ProjectMenuRoot;};

.cpp

#include "SlashEditor.h"#include "LevelEditor.h"
#include "SlashEditorStyle.h"
#include "PixHistogram/PixHistogramWidget.h"#define LOCTEXT_NAMESPACE "FSlashEditorModule"TSharedRef<FWorkspaceItem> FSlashEditorModule::ProjectMenuRoot = FWorkspaceItem::NewGroup(FText::FromString("ProjectToolsRoot"));
void FSlashEditorModule::StartupModule()
{FSlashEditorStyle::Register();FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");const TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());MenuExtender->AddMenuBarExtension("Help",EExtensionHook::Before,nullptr,FMenuBarExtensionDelegate::CreateRaw(this, &FSlashEditorModule::AddProjectToolsMenu));LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);FGlobalTabmanager::Get()->RegisterNomadTabSpawner(FName("PixHistogramTab"),FOnSpawnTab::CreateRaw(this,&FSlashEditorModule::SpawnPixHistogramTab)).SetGroup(ProjectMenuRoot).SetDisplayName(FText::FromString(TEXT("PixHistogram")));
}void FSlashEditorModule::ShutdownModule()
{FSlashEditorStyle::Unregister();
}void FSlashEditorModule::AddProjectToolsMenu(FMenuBarBuilder& MenuBuilder)
{MenuBuilder.AddPullDownMenu(LOCTEXT("Menu", "Project Menu"),LOCTEXT("ToolTip", "Open project menu"),FNewMenuDelegate::CreateRaw(this, &FSlashEditorModule::FillProjectToolsMenu),FName(TEXT("Project Menu")),FName(TEXT("ProjectMenu")));
}void FSlashEditorModule::FillProjectToolsMenu(FMenuBuilder& MenuBuilder)
{MenuBuilder.BeginSection("ProjectMenu", TAttribute<FText>(LOCTEXT("PixHistogramSectionName", "PixHistogram")));{FGlobalTabmanager::Get()->PopulateTabSpawnerMenu(MenuBuilder, FName("PixHistogramTab"));}MenuBuilder.EndSection();
}TSharedRef<SDockTab> FSlashEditorModule::SpawnPixHistogramTab(const FSpawnTabArgs& Args)
{TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab).TabRole(ETabRole::NomadTab)[SNew(SPixHistogramWidget)];return SpawnedTab;
}#undef LOCTEXT_NAMESPACEIMPLEMENT_MODULE(FSlashEditorModule, SlashEditor)

.Build.cs

            {"CoreUObject","Engine","Slate","SlateCore", "UMG","InputCore"}

最后成功添加按钮,点击即可得到开头结果:
在这里插入图片描述


http://www.ppmy.cn/news/1243917.html

相关文章

单片机学习2——流水灯的实现

#include<reg52.h>sbit LED P1^0; unsigned char i;void main() {while(1){LED 0;for(i0;i<100;i);LED 1;for(i0;i<100;i);} } RST是复位按钮&#xff0c;单击一下之后&#xff0c;程序就会跑到最开始的位置运行。 右侧的按钮是RUN按钮&#xff0c;单击下&…

西门子PLC与组态王无线通讯中如何设置从站

本方案主要详述了在多台西门子300PLC与组态王之间Modbus无线通讯中如何设置从站。方案中所用到的无线通讯终端是DTD434MC——欧美系PLC专用无线通讯终端。 一、方案概述 无线Modbus网络组成如下&#xff1a; 二、测试背景 ● PC端组态软件版本&#xff1a;组态王6.55 ● 默…

安卓使用MediaRecorder录制音频的详细使用

安卓使用MediaRecorder录制音频的详细使用 文章目录 安卓使用MediaRecorder录制音频的详细使用引言使用 MediaRecorder 的步骤常见问题及解决思路无法访问存储卡目录录制的音频文件没有声音录制过程中出现异常MediaRecorder无法正常启动录制的音质或者画质很差录制的文件无法播…

BEV+Transformer架构加速“上车”,智能驾驶市场变革开启

BEVTransformer成为了高阶智能驾驶领域最为火热的技术趋势。 近日&#xff0c;在2023年广州车展期间&#xff0c;不少车企及智能驾驶厂商都发布了BEVTransformer方案。其中&#xff0c;极越01已经实现了“BEVTransformer”的“纯视觉”方案的量产&#xff0c;成为国内唯一量产…

招投标信息可以用CRM系统来查看吗?

对于B2B企业来说获客难、获客成本高是共识。做大客户的企业通过招投标获取商机是一个重要获客途径&#xff0c;然而传统方式管理招投标信息问题很多&#xff0c;例如资料丢失、手工录入出错、信息分散、信息查找费时费力。为了解决这些难题小编推荐CRM系统&#xff0c;CRM系统需…

Redis 两种持久化方式 AOF 和 RDB

目录 一、Redis 的持久化 二、Redis 的持久化方式 RDB RDB 介绍 RDB 的触发方式&#xff1a;. 三、RDB的文件生成策略 四、Save 和 Bgsave 命令的区别 六、RDB 最佳配置 七、触发机制-不容忽略方式 AOF 一、AOF介绍 二、RDB所存在的问题 三、AOF 三种策略 四、AOF…

作为IT行业的过来人,宝贵的经验分享给刚入行的你

恍然间&#xff0c;发现自己已经在这个行业五年之久&#xff0c;回顾过往&#xff0c;思绪良多&#xff0c;一路走来&#xff0c;或多或少都经历过一些坎坷&#xff0c;也碰到过不少大大小小的困难。在此就不多加叙述了。 本篇文章主要想写给刚入门的程序员几个忠告&#xff0…

Linux shell编程学习笔记30:打造彩色的选项菜单

1 需求分析 在 Linux shell编程学习笔记21&#xff1a;用select in循环语句打造菜单https://blog.csdn.net/Purpleendurer/article/details/134212033?spm1001.2014.3001.5501 中&#xff0c;我们利用select in循环语句打造的菜单中&#xff0c;菜单项都是用系统设置的颜色配…