SWidget创建的实例
// 具体代码见百度网盘TestReflection /***************************** SSURAAR_MainWnd.h ********************************/ #include "CoreMinimal.h" #include "Slate.h" #include "Engine/RendererSettings.h" DECLARE_DELEGATE_OneParam(FOnCustomStencilChanged, ECustomDepthStencil::Type) DECLARE_DELEGATE_OneParam(FOnAlphaChannelModeChanged, EAlphaChannelMode::Type) DECLARE_DELEGATE_OneParam(FOnFrameBufferPixelFormatChanged, EDefaultBackBufferPixelFormat::Type) class SSURAAR_MainWnd : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SSURAAR_MainWnd){} SLATE_END_ARGS() void Construct(const FArguments& Args); TSharedRef<SWidget> MakeCustomStencilCombo(FOnCustomStencilChanged OnChanged, TAttribute<ECustomDepthStencil::Type> SelectedEnum, EOrientation Orientation); TSharedRef<SWidget> MakeAlphaChannelModeCombo(FOnAlphaChannelModeChanged OnChanged, TAttribute<EAlphaChannelMode::Type> SelectedEnum, EOrientation Orientation); TSharedRef<SWidget> MakeFrameBufferPixelFormatCombo(FOnFrameBufferPixelFormatChanged OnChanged, TAttribute<EDefaultBackBufferPixelFormat::Type> SelectedEnum, EOrientation Orientation); TSharedRef<SWidget> MakeSURACameraDriverDetails(); private: EVisibility CheckSuraCameraDriverExist() const; }; /********************************** SSURAAR_MainWnd.cpp ***********************************/ // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #include "SSURAAR_MainWnd.h" #include <Kismet/GameplayStatics.h> #include "SSuraEnumComboBox.h" #include "SURACameraDriver.h" #include "Customizations/SURACameraDriverDetailsCustomization.h" #define LOCTEXT_NAMESPACE "SURAAR_MainWnd" void SSURAAR_MainWnd::Construct(const FArguments& Args) { FText PlaceholderText = LOCTEXT("PlaceholderText", "Placeholder"); auto Settings = GetMutableDefault<URendererSettings>(); ChildSlot [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(SUniformGridPanel) .SlotPadding(TAttribute<FMargin>(FMargin(4.0f))) +SUniformGridPanel::Slot(0, 0) .VAlign(EVerticalAlignment::VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Custom Depth-Stencil Pass", "Custom Depth-Stencil Pass")) ] +SUniformGridPanel::Slot(1, 0) .HAlign(EHorizontalAlignment::HAlign_Fill) .VAlign(EVerticalAlignment::VAlign_Fill) [ MakeCustomStencilCombo( FOnCustomStencilChanged::CreateLambda([&](ECustomDepthStencil::Type stencil) { auto Settings = GetMutableDefault<URendererSettings>(); Settings->CustomDepthStencil = stencil; }), TAttribute<ECustomDepthStencil::Type>::Create(TAttribute<ECustomDepthStencil::Type>::FGetter::CreateLambda([]() { auto Settings = GetDefault<URendererSettings>(); return Settings->CustomDepthStencil.GetValue(); })), Orient_Vertical ) ] +SUniformGridPanel::Slot(0, 1) .VAlign(EVerticalAlignment::VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Enable alpha channel support", "Enable alpha channel support in post processing")) ] +SUniformGridPanel::Slot(1, 1) .HAlign(EHorizontalAlignment::HAlign_Fill) .VAlign(EVerticalAlignment::VAlign_Fill) [ MakeAlphaChannelModeCombo( FOnAlphaChannelModeChanged::CreateLambda([&](EAlphaChannelMode::Type AlphaMode) { auto Settings = GetMutableDefault<URendererSettings>(); Settings->bEnableAlphaChannelInPostProcessing = AlphaMode; }), TAttribute<EAlphaChannelMode::Type>::Create(TAttribute<EAlphaChannelMode::Type>::FGetter::CreateLambda([]() { auto Settings = GetDefault<URendererSettings>(); return Settings->bEnableAlphaChannelInPostProcessing.GetValue(); })), Orient_Vertical ) ] +SUniformGridPanel::Slot(0, 2) .VAlign(EVerticalAlignment::VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Frame Buffer Pixel Format", "Frame Buffer Pixel Format")) ] +SUniformGridPanel::Slot(1, 2) .HAlign(EHorizontalAlignment::HAlign_Fill) .VAlign(EVerticalAlignment::VAlign_Fill) [ MakeFrameBufferPixelFormatCombo( FOnFrameBufferPixelFormatChanged::CreateLambda([&](EDefaultBackBufferPixelFormat::Type PixelFormat) { auto Settings = GetMutableDefault<URendererSettings>(); Settings->DefaultBackBufferPixelFormat = PixelFormat; }), TAttribute<EDefaultBackBufferPixelFormat::Type>::Create(TAttribute<EDefaultBackBufferPixelFormat::Type>::FGetter::CreateLambda([]() { auto Settings = GetDefault<URendererSettings>(); return Settings->DefaultBackBufferPixelFormat.GetValue(); })), Orient_Vertical ) ] ] +SVerticalBox::Slot() .AutoHeight() [ //SNew(STextBlock).Text(PlaceholderText) SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() [ SNew(SButton) // button for checking SURAAcameraDriver .Visibility_Raw(this, &SSURAAR_MainWnd::CheckSuraCameraDriverExist) .Content() [ SNew(STextBlock).Text(LOCTEXT("SetupEnv", "Setup Environment")) ] ] ] + SVerticalBox::Slot() [ MakeSURACameraDriverDetails() ] /*+SVerticalBox::Slot() [ SNew(STextBlock).Text(PlaceholderText) ]*/ ]; } TSharedRef<SWidget> SSURAAR_MainWnd::MakeCustomStencilCombo( FOnCustomStencilChanged OnChanged, TAttribute<ECustomDepthStencil::Type> SelectedEnum, EOrientation Orientation ) { TArray<SSuraEnumCombo<ECustomDepthStencil::Type>::FComboOption> CustomStencilInfo; CustomStencilInfo.Add(SSuraEnumCombo<ECustomDepthStencil::Type>::FComboOption( ECustomDepthStencil::Disabled, FSlateIcon(), LOCTEXT("CustomStencil.Disabled", "Disabled"))); CustomStencilInfo.Add(SSuraEnumCombo<ECustomDepthStencil::Type>::FComboOption( ECustomDepthStencil::Enabled, FSlateIcon(), LOCTEXT("CustomStencil.Enabled", "Enabled"))); CustomStencilInfo.Add(SSuraEnumCombo<ECustomDepthStencil::Type>::FComboOption( ECustomDepthStencil::EnabledOnDemand, FSlateIcon(), LOCTEXT("CustomStencil.EnabledOnDemand", "EnabledOnDemand"))); CustomStencilInfo.Add(SSuraEnumCombo<ECustomDepthStencil::Type>::FComboOption( ECustomDepthStencil::EnabledWithStencil, FSlateIcon(), LOCTEXT("CustomStencil.EnabledWithStencil", "EnabledWithStencil"))); return SNew(SSuraEnumCombo<ECustomDepthStencil::Type>, MoveTemp(CustomStencilInfo)) .SelectedEnum(SelectedEnum) .OnEnumChanged(OnChanged) .Orientation(Orientation); //.ToolTip(IDocumentation::Get()->CreateToolTip(LOCTEXT("GraphicsPresetTooltip", "Choose the graphical level to target (high-end only or scalable from low-end on up)."), NULL, TEXT("Shared/Editor/Settings/TargetHardware"), TEXT("GraphicalLevel"))); } TSharedRef<SWidget> SSURAAR_MainWnd::MakeAlphaChannelModeCombo( FOnAlphaChannelModeChanged OnChanged, TAttribute<EAlphaChannelMode::Type> SelectedEnum, EOrientation Orientation ) { TArray<SSuraEnumCombo<EAlphaChannelMode::Type>::FComboOption> AlphaChannelModeInfo; AlphaChannelModeInfo.Add(SSuraEnumCombo<EAlphaChannelMode::Type>::FComboOption( EAlphaChannelMode::Disabled, FSlateIcon(), LOCTEXT("AlphaChannelMode.Disabled", "Disabled"))); AlphaChannelModeInfo.Add(SSuraEnumCombo<EAlphaChannelMode::Type>::FComboOption( EAlphaChannelMode::LinearColorSpaceOnly, FSlateIcon(), LOCTEXT("AlphaChannelMode.LinearColorSpaceOnly", "LinearColorSpaceOnly"))); AlphaChannelModeInfo.Add(SSuraEnumCombo<EAlphaChannelMode::Type>::FComboOption( EAlphaChannelMode::AllowThroughTonemapper, FSlateIcon(), LOCTEXT("AlphaChannelMode.AllowThroughTonemapper", "AllowThroughTonemapper"))); return SNew(SSuraEnumCombo<EAlphaChannelMode::Type>, MoveTemp(AlphaChannelModeInfo)) .SelectedEnum(SelectedEnum) .OnEnumChanged(OnChanged) .Orientation(Orientation); //.ToolTip(IDocumentation::Get()->CreateToolTip(LOCTEXT("GraphicsPresetTooltip", "Choose the graphical level to target (high-end only or scalable from low-end on up)."), NULL, TEXT("Shared/Editor/Settings/TargetHardware"), TEXT("GraphicalLevel"))); } TSharedRef<SWidget> SSURAAR_MainWnd::MakeFrameBufferPixelFormatCombo( FOnFrameBufferPixelFormatChanged OnChanged, TAttribute<EDefaultBackBufferPixelFormat::Type> SelectedEnum, EOrientation Orientation ) { TArray<SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption> FramePixelFormatInfo; FramePixelFormatInfo.Add(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption( EDefaultBackBufferPixelFormat::DBBPF_B8G8R8A8, FSlateIcon(), LOCTEXT("FramePixelFormat.DBBPF_B8G8R8A8", "8bit RGBA"))); FramePixelFormatInfo.Add(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption( EDefaultBackBufferPixelFormat::DBBPF_A16B16G16R16_DEPRECATED, FSlateIcon(), LOCTEXT("FramePixelFormat.DBBPF_A16B16G16R16_DEPRECATED", "DEPRECATED - 16bit RGBA"), false)); FramePixelFormatInfo.Add(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption( EDefaultBackBufferPixelFormat::DBBPF_FloatRGB_DEPRECATED, FSlateIcon(), LOCTEXT("FramePixelFormat.DBBPF_FloatRGB_DEPRECATED", "DEPRECATED - Float RGB"), false)); FramePixelFormatInfo.Add(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption( EDefaultBackBufferPixelFormat::DBBPF_FloatRGBA, FSlateIcon(), LOCTEXT("FramePixelFormat.DBBPF_FloatRGBA", "Float RGBA"))); FramePixelFormatInfo.Add(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>::FComboOption( EDefaultBackBufferPixelFormat::DBBPF_A2B10G10R10, FSlateIcon(), LOCTEXT("FramePixelFormat.DBBPF_A2B10G10R10", "10bit RGB, 2bit Alpha"))); return SNew(SSuraEnumCombo<EDefaultBackBufferPixelFormat::Type>, MoveTemp(FramePixelFormatInfo)) .SelectedEnum(SelectedEnum) .OnEnumChanged(OnChanged) .Orientation(Orientation); //.ToolTip(IDocumentation::Get()->CreateToolTip(LOCTEXT("GraphicsPresetTooltip", "Choose the graphical level to target (high-end only or scalable from low-end on up)."), NULL, TEXT("Shared/Editor/Settings/TargetHardware"), TEXT("GraphicalLevel"))); } EVisibility SSURAAR_MainWnd::CheckSuraCameraDriverExist() const { TArray<AActor*> OutActors; if (GWorld) { UGameplayStatics::GetAllActorsOfClass(GWorld, ASURACameraDriver::StaticClass(), OutActors); } if (OutActors.Num() == 0) { UE_LOG(LogTemp, Log, TEXT("No SuraCameraDriver found.")); return EVisibility::Visible; } else { return EVisibility::Collapsed; } } TSharedRef<SWidget> SSURAAR_MainWnd::MakeSURACameraDriverDetails( ) { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor")); FDetailsViewArgs Args; Args.bAllowSearch = false; Args.NameAreaSettings = FDetailsViewArgs::HideNameArea; TSharedRef<IDetailsView> View = PropertyModule.CreateDetailView(Args); View->RegisterInstancedCustomPropertyLayout(ASURACameraDriver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FSURACameraDriverDetailsCustomization::MakeInstance)); TArray<AActor*> OutActors; if (GWorld) { UGameplayStatics::GetAllActorsOfClass(GWorld, ASURACameraDriver::StaticClass(), OutActors); } if (OutActors.Num() > 0) { View->SetObject(OutActors[0], true); } return View; } #undef LOCTEXT_NAMESPACE
config/配置存取
UCLASS(Config=AnjGameSettings, ConfigDoNotCheckDefaults/DefaultConfig) class UAnjGameSettings : UDevelopSettings // [OR] UEditorSettings [OR] class AAnjGameSettings : Axxx { UPROPERTY(Config) int BroadcastPort = 8888;
AAnjGameSettings(); /** Gets the settings container name for the settings, either Project or Editor */ virtual FName GetContainerName() const override { return TEXT("Project"); } /** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */ virtual FName GetCategoryName() const override { return TEXT("DataDrivenSettings"); } /** The unique name for your section of settings, uses the class's FName. */ virtual FName GetSectionName() const override { return TEXT("DataDrivenSettings"); } }
代码中Config=AnjGameSettings用来设定class中带有Config meta信息的Property要写入AnjGameSettings.ini文件。参考官网文档,这个Config叫做Specifier,指定Configuration Categories,Categories列表在官网能看到。
AAnjGameSettings()构造函数可以用来设定Property的默认值:
AAnjGameSettings()构造函数可以用来设定Property的默认值:
UAnjGameSettings::UAnjGameSettings( ) { if (GConfig) { FString IPStr; if (!GConfig->GetString(TEXT("/Script/SuraForAnj.AnjGameSettings"), TEXT("LocalIP"), IPStr, GetClass()->GetConfigName())) { bool canBind = false; TSharedRef<FInternetAddr> localIp = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, canBind); FIPv4Endpoint LocalEndpoint = FIPv4Endpoint(localIp); LocalIP = LocalEndpoint.Address.ToString(); SaveConfig(); } } }
注意上面代码中的GetClass()->GetConfigName(),它返回Config指定的AnjGameSettings.ini文件的存储路径(也可以直接写GGameIni、GEngineIni等值)。SaveConfig()是UObject的函数,注意其Filename参数未指定时,内部实现是设定为GetClass()->GetConfigName()。
ConfigDoNotCheckDefaults与DefaultConfig的区别:
UpdateDefaultConfigFile()函数类似上面的SaveConfig(),但会把值写入AnjGameSettingsDefault.ini文件。而在调用SaveConfig()时,如为DefaultConfig,则当Property的值与xxxDefault.ini文件里的值一致时,xxx.ini文件里Property属性行会被删除,这是因为xxxDefault.ini文件先被读取,然后是xxx.ini,所以与xxxDefault.ini里一致的属性,没必要再往xxx.ini写一条记录。而指定了ConfigDoNotCheckDefaults时,SaveConfig()不检查有无Default.ini文件及里面的属性值,直接更新xxx.ini文件。
UpdateDefaultConfigFile()函数类似上面的SaveConfig(),但会把值写入AnjGameSettingsDefault.ini文件。而在调用SaveConfig()时,如为DefaultConfig,则当Property的值与xxxDefault.ini文件里的值一致时,xxx.ini文件里Property属性行会被删除,这是因为xxxDefault.ini文件先被读取,然后是xxx.ini,所以与xxxDefault.ini里一致的属性,没必要再往xxx.ini写一条记录。而指定了ConfigDoNotCheckDefaults时,SaveConfig()不检查有无Default.ini文件及里面的属性值,直接更新xxx.ini文件。
从UDeveloperSettings派生的UObject,可以继承其能够在UE编辑器的ProjectSettings/EditorPreferences->Category->Section->(UPROPERTY.Category)里直接编辑属性的能力。
- GetContainerName():返回TEXT("Project")或者TEXT("Editor"),标识属性要在Project Settings菜单还是Editor Preferences菜单里设置。
- GetCategoryName():指定Settings界面的Category,比如Project Settings里的Project/Game/Plugins等。
- GetSectionName():指定该UObject的config属性要写入的界面段落,在该段落里面则是根据Property自己的Catogry meta信息来排布。
关于config信息的读取,参考:
To access the developer settings in C++ we use the CDO (Class Default Object) as that is already automatically instanced for us and accessed using GetDefault<T>();
To access the developer settings in C++ we use the CDO (Class Default Object) as that is already automatically instanced for us and accessed using GetDefault<T>();
void USSaveGameSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection);
const USSaveGameSettings* SGSettings = GetDefault<USSaveGameSettings>(); // Access via CDO // Access defaults from DefaultGame.ini SlotName = SGSettings->SaveSlotName;
// Make sure it's loaded into memory .Get() only resolves if already loaded previously elsewhere in code UDataTable* DummyTable = SGSettings->DummyTablePath.LoadSynchronous(); }
有一个全部自定义处理config的参考
获取本地IP地址
// Module: Sockets.build.cs #include "SocketSubsystem.h" #include "IPAddress.h" #include "Interfaces/IPv4/IPv4Endpoint.h" bool canBind = false; TSharedRef<FInternetAddr> localIp = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, canBind); FIPv4Endpoint LocalEndpoint = FIPv4Endpoint(localIp); LocalIP = LocalEndpoint.Address.ToString();
ini文件读写
https://blog.51cto.com/hanzhichao/3201113 from configparser import ConfigParser conf = ConfigParser() conf.read(configFilePath, encoding=cur_encoding) print(conf['section']['key']) ''' LutConfig.py ''' from pathlib import Path from typing import * import chardet from configparser import ConfigParser def GetDefaultConfigFilePath() -> Path: return Path(__file__).parent.absolute() / 'Windows/TestLutPro/Config/DefaultGame.ini' def GetConfig(configFilePath: str) -> dict: # 首先二进制方式打开文件 with open(configFilePath, 'rb') as frb: # 检测编码方式 cur_encoding = chardet.detect(frb.read())['encoding'] # 指定文件编码方式 conf = ConfigParser() conf.read(configFilePath, encoding=cur_encoding) configResult = {} section = '/Script/ApplyLutTools.ApplyLutSettings' if conf.has_option(section, 'ServerIP'): configResult['ServerIP'] = conf[section]['ServerIP'] if conf.has_option(section, 'ServerPort'): configResult['ServerPort'] = conf[section].getint('ServerPort') if conf.has_option(section, 'LocalListeningIP'): configResult['LocalListeningIP'] = conf[section]['LocalListeningIP'] return configResult def GetDefaultConfig() -> dict: return GetConfig(GetDefaultConfigFilePath())
Details视图定制
一个Actor或Property的Details信息的展示界面,可以定制。这项工作涉及几个基本的接口:
- IDetailsView,其派生自SCompoundWidget。能看到的Details界面就是这个接口的实例。
- SDetailsView->SDetailsViewBase->IDetailsViewPrivate->IDetailsView
- FPropertyEditorModule::CreateDetailView(const FDetailsViewArgs& DetailsViewArgs); // 用户填写参数后调用该函数拿到一个IDetailsView实例。
- IDetailLayoutBuilder接口是一个工具类,它定义了一套支持Category的显示控制、Property的显示控制、隐藏等等功能的接口。至于这些功能的实际表现由具体实现来完成
- IDetailLayoutBuilder接口在UE4里有一个实现:FDetailLayoutBuilderImpl。这个实现按照一套确定的风格完成了上面说的功能概念,UE4里如上功能均由该实现来完成
- UE4额外提供了一个IDetailCustomization接口,用户需要实现其CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)函数,目的是集中在这里将IDetailLayoutBuilder需要的数据输入给它。这也是通常用户要定制Details视图时唯一需要实现的接口。
- FPropertyEditorModule::RegisterCustomClassLayout()实现将IDetailCustomization信息提交给引擎的功能。
以上需要引用【PropertyEditor】模块。
代码示例:
针对特定Class类型的所有实例定制其Details面板的情形:
针对特定Class类型的所有实例定制其Details面板的情形:
StarupModule() { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor")); // Custom Class Layouts auto AddCustomClass = [this, InPropertyModule = &PropertyModule](FName Name, FOnGetDetailCustomizationInstance InstanceGetter) { InPropertyModule->RegisterCustomClassLayout(Name, InstanceGetter); CustomClassLayoutNames.Add(Name); }; AddCustomClass(ASURACameraDriver::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FSURACameraDriverDetailsCustomization::MakeInstance)); }
ShutdownModule() { if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) { FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
for (FName ClassName : CustomClassLayoutNames) { PropertyModule.UnregisterCustomClassLayout(ClassName); } CustomClassLayoutNames.Reset(); } }
// .h TArray<FName> CustomClassLayoutNames;
针对特定Class类型的某个实例定制Details面板。需要在该实例对应的IDetailsView中设置,详见代码 Customizations/SURACameraDriverDetailsCustomization代码
IDetailsView面板与UObject建立关联:
TSharedRef<SWidget> SSURAAR_MainWnd::MakeSURACameraDriverDetails( ) { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
FDetailsViewArgs Args; Args.bAllowSearch = false; // 隐藏Details面板的搜索框 Args.NameAreaSettings = FDetailsViewArgs::HideNameArea; // 隐藏Details面板的角色名框 TSharedRef<IDetailsView> View = PropertyModule.CreateDetailView(Args); View->RegisterInstancedCustomPropertyLayout(ASURACameraDriver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FSURACameraDriverDetailsCustomization::MakeInstance));
TArray<AActor*> OutActors; if (GWorld) { UGameplayStatics::GetAllActorsOfClass(GWorld, ASURACameraDriver::StaticClass(), OutActors); } if (OutActors.Num() > 0) { View->SetObject(OutActors[0], true); }
return View; }