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

news2024/11/27 0:18:23

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 drawing
	FSlateDrawElement::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 ruler
	FLinearColor 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 ruler
	int 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 Axis
	FLinearColor 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 ruler
	int 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 coordinate
	const 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 labeling
	FSlateDrawElement::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 labeling
	FSlateDrawElement::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 reminder
	const 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_NAMESPACE
    
IMPLEMENT_MODULE(FSlashEditorModule, SlashEditor)

.Build.cs

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1257458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fastjson和jackson序列化的使用案例

简单记录一下一个fastjson框架和jackson进行序列化的使用案例&#xff1a; 原json字符串&#xff1a; “{“lockCount”:”{1:790,113:1,2:0,211:0,101:1328,118:8,137:0,301:0,302:0}“,“inventoryCount”:”{1:25062,113:2,2:10000,211:2,101:11034,118:9,137:40,301:903914…

如何查看电脑版Office的有效期

有时候点击Office账户看不到有效期信息&#xff0c;那么如何查看呢&#xff0c;其实用一条命令就可以查看。 首选WinR运行&#xff0c;输入cmd回车&#xff0c;然后输入下面的命令&#xff1a; cscript “C:\Program Files\Microsoft Office\Office16\ospp.vbs” /dstatus当然…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑氢储一体化协同的综合能源系统低碳优化》

这个标题涉及到考虑了多个方面的能源系统优化&#xff0c;其中关键的关键词包括"氢储一体化"、"协同"、"综合能源系统"和"低碳优化"。以下是对这些关键词的解读&#xff1a; 氢储一体化&#xff1a; 氢储存&#xff1a; 指的是氢气的存…

【开源】基于Vue+SpringBoot的企业项目合同信息系统

项目编号&#xff1a; S 046 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S046&#xff0c;文末获取源码。} 项目编号&#xff1a;S046&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合…

LeetCode [简单]118. 杨辉三角

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 public class Solution {public IList<IList<int>> Generate(int numRows) {List<IList<int>> res new …

c# 简单web api接口实例源码分析

CreateHostBuilder(args).Build().Run();这句语句处于c#webapi程序的第一句&#xff0c;它的作用是&#xff1a;启动接口的三个步骤&#xff1a; 创建一个HostBuilder对象。执行IHostBuilder.Build()方法创建IHost对象。执行IHost.Run()方法启动。 创建和配置Host&#xff08;…

sklearn中tfidf的计算与手工计算不同详解

sklearn中tfidf的计算与手工计算不同详解 引言&#xff1a;本周数据仓库与数据挖掘课程布置了word2vec的课程作业&#xff0c;要求是手动计算corpus中各个词的tfidf&#xff0c;并用sklearn验证自己计算的结果。但是博主手动计算的结果无论如何也与sklearn中的结果无法对应&…

ChatGLM 6B 部署及微调 【干货】

代码地址、模型地址、安装环境&#xff1a;Ubuntu20.04&#xff0c;RTX3060 12G 一、部署 1.1 下载代码 cd /opt git clone https://github.com/THUDM/ChatGLM2-6B1.2 下载模型 将模型下载后放到项目文件夹内 git lfs install # 确认安装了lfs&#xff0c;或者直接到项目地…

基于ssm的网上订餐系统

一、系统架构 前端&#xff1a;jsp | js | css | jquery 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.7 | mysql | maven | tomcat 二、代码与数据库 三、功能介绍 01. web端-首页 02. web端-我的餐车 03. web端-我的订单 04. web端-用户中心 05. web…

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

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

tomcat-pass-getshell 弱口令 漏洞复现

tomcat-pass-getshell 弱口令 漏洞复现 名称: tomcat-pass-getshell 弱口令 描述: Tomcat是Apache 软件基金会&#xff08;Apache Software Foundation&#xff09;的Jakarta 项目中的一个核心项目&#xff0c;由Apache、Sun 和其他一些公司及个人共同开发而成。 通过弱口令登…

2024 IEEE Fellow名单公布,上百位华人学者入选!

就在近日&#xff0c;美国电子电气工程师学会&#xff08;Institute of Electrical and Electronic Engineers&#xff0c;IEEE&#xff09;公布了新一届Fellow名单。 IEEE Fellow为学会最高等级会员&#xff0c;是IEEE授予成员的最高荣誉&#xff0c;旨在表彰那些在电气、电子…

内网隧道学习

默认密码&#xff1a;hongrisec2019 一.环境搭建 网卡学习 一个网卡一个分段&#xff0c;想象成一个管道 192.168.52一段 192.168.150一段 仅主机模式保证不予外界连通&#xff0c;保证恶意操作不会跑到真实机之上 52段是内部通信&#xff0c;150段属于服务器&#xff08;…

23款奔驰E300L升级23P驾驶辅助 智驾出行 缓解开车疲劳

辅助驾驶越来越多的被大家所青睐&#xff01;为了提升驾驶安全性和舒适便捷性奔驰改装原厂半自动驾驶23P辅助系统 23P智能辅助驾驶系统还是很有必要的&#xff0c;因为在跑高速的时候可以使用23P智能驾驶的自动保持车速&#xff0c;保持车距&#xff0c;车道自动居中行驶以及自…

小型内衣洗衣机什么牌子好?口碑最好的小型洗衣机

很多人会觉得内衣洗衣机是智商税&#xff0c;洗个内衣只需要两分钟的事情&#xff0c;需要花个几百块钱去入手一个洗衣机吗&#xff1f;然而清洗贴身衣物的并不是一件简单的事情&#xff0c;如果只是简单的搓洗&#xff0c;内裤上看不见的细菌也无法消除&#xff0c;而且对来生…

英特尔工作站:助力专业用户实现高效创作

原创 | 文 BFT机器人 英特尔工作站是由全球知名的英特尔公司设计和开发的一款计算平台。英特尔在工作站处理器领域将其产品分为性能型和移动型两类&#xff0c;它的诞生旨在满足专业用户在科学、工程、设计等领域对高性能计算的需求。英特尔工作站配备了最新的英特尔处理器、大…

关于卸载亚信安全防火墙软件

电脑根据需求上安装亚信安全防火墙,不需要时,要卸载需要密码 关于卸载亚信安全防火墙客户端-适用于win10和win11的方法(系统64位). 亲测有效: 1.电脑上winR,运行输入msconfig 2.勾选引导中的安全引导,然后重新启动,进入安全模式. 3.winR输入regedit,打开注册表编辑器进入/Mis…

苹果用户如何恢复手机数据?盘点3个实用方法!

随着手机功能的不断增强和应用程序的不断丰富&#xff0c;人们越来越依赖手机&#xff0c;离不开手机。然而&#xff0c;有时因为误操作、系统故障或者数据丢失等原因&#xff0c;我们需要找回手机上丢失的数据。那么&#xff0c;苹果用户如何恢复手机数据呢&#xff1f;本文将…

java开发必备的Vue知识点和技能

vue介绍 什么是Vue&#xff1f;vue就是一款用于构建用户界面的渐进式的JavaScript框架。&#xff08;官方&#xff1a;https://cn.vuejs.org/&#xff09; 框架&#xff1a;就是一套完整的项目解决方案&#xff0c;用于快速构建项目。 优点&#xff1a;大大提升前端项目的开…

Java基础之集合类

Java基础之集合类 一、集合的框架1.1、集合概述1.2、集合与数组区别1.3、数组的缺点&#xff1a;1.4、常用集合分类1.5、Collection常用方法 二、List集合2.1、ArrayList2.2、LinkedList2.3、Vector2.4、区别 三、Set集合3.1、HashSet集合3.2、LinkedHashSet集合3.3、TreeSet集…