ue5 创建多列StreeView的方法与理解
创建StreeView的多列样式怎么就像是创建单行单列差不多?貌似就是在单行单列中加入了多列widget?
示例代码
DetailTabWidget
#pragma once
#include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget
{SLATE_BEGIN_ARGS(SDetailTabWidget){}SLATE_END_ARGS()void Construct(const FArguments& InArgs);private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;TSharedPtr<STreeView<TSharedRef<FTreeViewItemBase>>> TreeViewItem;TArray<TSharedRef<FTreeViewItemBase>> TreeViewSource;public:TSharedRef<class ITableRow> GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase, const TSharedRef< class STableViewBase >& TableViewBase);void OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase, TArray<TSharedRef<FTreeViewItemBase>>& OutChildren);void SetItemData();
};
#include "DetailTabWidget.h"#include "TreeViewItemBaseBool.h"
#include "Widgets/Layout/SWidgetSwitcher.h"void SDetailTabWidget::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("This DetailTab Panel"))]+SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SAssignNew(TreeViewItem,STreeView<TSharedRef<FTreeViewItemBase>>).TreeItemsSource(&TreeViewSource)//委托,生成每一行,绑定自定义方法(事件),触发时生成.OnGenerateRow(this, &SDetailTabWidget::GenerateTreeViewRow).OnGetChildren(this,&SDetailTabWidget::OnGetChildren).HeaderRow(SNew(SHeaderRow)+SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10,5,5,5))[SNew(STextBlock).Text(FText::FromString(TEXT("名 称")))]]+SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10,5,5,5))[SNew(STextBlock).Text(FText::FromString(TEXT("属 性")))]])]];SetItemData();
}TSharedRef<class ITableRow> SDetailTabWidget::GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase,const TSharedRef<class STableViewBase>& TableViewBase)
{return SNew(SMultiTableRow, TableViewBase,ItemBase);
}void SDetailTabWidget::OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase,TArray<TSharedRef<FTreeViewItemBase>>& OutChildren)
{ItemBase->Getchildren(OutChildren);
}void SDetailTabWidget::SetItemData()
{TSharedRef<FTreeViewItemBaseBool> BoolItem = MakeShareable(new FTreeViewItemBaseBool());TreeViewSource.Add(BoolItem);TreeViewItem->RequestTreeRefresh();WidgetSwitcher->SetActiveWidgetIndex(1);}
TreeViewItemBase
#pragma once// Item
class FTreeViewItemBase : public TSharedFromThis<FTreeViewItemBase>
{
public:virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;void Getchildren(TArray<TSharedRef<FTreeViewItemBase>>& OutChildren) const{OutChildren = Children;}
private:TArray<TSharedRef<FTreeViewItemBase>> Children;
};// Row单行多列
class SMultiTableRow : public SMultiColumnTableRow<TSharedRef<FTreeViewItemBase>>
{SLATE_BEGIN_ARGS(SMultiTableRow){}SLATE_END_ARGS()virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase);
private:TWeakPtr<FTreeViewItemBase> TreeViewItemBasePtr;
};
#include "TreeViewItemBase.h"TSharedRef<SWidget> SMultiTableRow::GenerateWidgetForColumn(const FName& ColumnName)
{if (ColumnName.IsEqual((TEXT("Name")))){return TreeViewItemBasePtr.Pin()->MakeNameWidget();}if (ColumnName.IsEqual(TEXT("Value"))){return TreeViewItemBasePtr.Pin()->MakeValueWidget(); }return SNullWidget::NullWidget;
}void SMultiTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView,const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase)
{TreeViewItemBasePtr = InTreeViewItemBase;STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,0,0,0));SMultiColumnTableRow::Construct(ParentArgs, InOwnerTableView);
}
TreeViewItemBaseBool
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseBool : public FTreeViewItemBase
{
public:virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool Value;};
#include "TreeViewItemBaseBool.h"TSharedRef<SWidget> FTreeViewItemBaseBool::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FTreeViewItemBaseBool::MakeValueWidget()
{return SNew(SCheckBox).OnCheckStateChanged(this, &FTreeViewItemBaseBool::OnCheckStateChanged).IsChecked(this, &FTreeViewItemBaseBool::GetCheckBoxState);}void FTreeViewItemBaseBool::OnCheckStateChanged(ECheckBoxState CheckState)
{Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FTreeViewItemBaseBool::GetCheckBoxState() const
{return Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
目录结构:
必备条件
StreeView的多列创建需要的必备条件:
数据基类
CustomItemBase
#pragma once
/*
----------------------------------
| Name | Value |
----------------------------------
| 香蕉 | 真正的香蕉 |
----------------------------------
*/// 构建一个基类,相当于创建一个空的价签,其中包括(名字:价格:),具体怎么填由子类决定
class FCustomItemBase : public TSharedFromThis<FCustomItemBase>
{
public:virtual ~FCustomItemBase() {}// 比如要卖香蕉:名字:香蕉 ,Value :价格virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;//当展示时需要先获取到这个价签后才能知道这是为哪个商品准备的价签(即:名字,价格)void GetChildrens(TArray<TSharedRef<FCustomItemBase>>& OutChildren) const{ OutChildren = Childrens;};private://用于保存传入的参数并通过OutChildren传出去TArray<TSharedRef<FCustomItemBase>> Childrens;
};/*
----------------------------------
| Name | Value |
----------------------------------
FCustomItemBase中有两个属性(name、Value),所以制作价签时就需要预留两个空位,使用SMultiColumnTableRow多列样式
创建继承至class SMultiColumnTableRow的类,查看基类样式为SMultiColumnTableRow : public STableRow<ItemType>
解释为: S类名 :public 基类<价签的引用>,有几个属性就安排几个位置(即:有两个属性,就安排两列),这是S类,所以需要S类的构造方法
如果是单列:STableRow,每行就只能放一个属性,明白了吗?
----------
| Name |
----------
*/class SMultiDetailTableRow : public SMultiColumnTableRow<TSharedRef<FCustomItemBase>>
{SLATE_BEGIN_ARGS(SMultiDetailTableRow){}SLATE_END_ARGS()// 父类SMultiColumnTableRow中的方法,动态生成不同列的显示内容,必须实现的方法virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& InColumnName ) override;// 父类方法中默认两个参数,第三个参数是因为创建时必须有FCustomItemBase才能正确显示void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView,TSharedRef<FCustomItemBase> InCustomItemBase);
private:// 这个弱指针是为了初始化时将传进来的FCustomItemBase赋值给CustomItemBase用于保存TWeakPtr<FCustomItemBase> CustomItemBase;
};
#include "CustomItemBase.h"//一行多列
TSharedRef<SWidget> SMultiDetailTableRow::GenerateWidgetForColumn(const FName& InColumnName)
{// 判断InColumnName,如果传入的是名字,就在价签的name中添加名字if (InColumnName.IsEqual(TEXT("Name"))){return CustomItemBase.Pin()->MakeNameWidget();}// 判断InColumnName,如果传入的是Value,就在价签的Value中添加价格if ((InColumnName.IsEqual(TEXT("Value")))){return CustomItemBase.Pin()->MakeValueWidget();}// 空widget用于占位return SNullWidget::NullWidget;
}//还是创建了一行两列,streeview中的多个行还是需要在生成树的类中创建
void SMultiDetailTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView,TSharedRef<FCustomItemBase> InCustomItemBase)
{//赋值保存CustomItemBase = InCustomItemBase;//STableRow::FArguments()是这个是原生的风格样式,生成时的样式布局(可替换)STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,8,0,0));//调用基类的构造函数,查看基类中的Construct方法SMultiColumnTableRow::Construct(ParentArgs, OwnerTableView);}
数据子类
CustomItemBool
#pragma once
#include "CustomItemBase.h"//价签的子类,父类中的价签已经印刷好了
class FCustomItemBool : public FCustomItemBase
{
public://创建后默认就是 falseFCustomItemBool():Value(false){}// 实现父类 CustomItemBase中的方法,做具体的事(即:具体的名字,具体的价格)virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool Value;
};
#include "CustomItemBool.h"TSharedRef<SWidget> FCustomItemBool::MakeNameWidget()
{//具体的名字Test Bool,显示中文必须用Text包裹:(TEXT("选中测试Bool")return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FCustomItemBool::MakeValueWidget()
{//这里创建的是选择框,可以是选择框、下拉框...Bool类的控件return SNew(SCheckBox)//绑定点击状态,是否点击了,这是一个事件SLATE_EVENT( FOnCheckStateChanged, OnCheckStateChanged )//相当于点击了就触发点击事件,将这个事件与回调函数OnCheckStateChanged绑定,点击就触发.OnCheckStateChanged(this, &FCustomItemBool::OnCheckStateChanged)//点击后的状态,是选中了还是取消了,查看SLATE_ATTRIBUTE( ECheckBoxState, IsChecked )后不是知道是啥//继续查看enum class ECheckBoxState : uint8,里面有三种状态,那这个IsChecked()的参数是不是就是要这个枚举?//因为这里是绑定,需要回调函数,所以创建一个ECheckBoxState类型的函数试试,结果懵对了.IsChecked(this, &FCustomItemBool::GetCheckBoxState);
}void FCustomItemBool::OnCheckStateChanged(ECheckBoxState CheckState)
{//既然是回调函数,那么当被触发后要做什么?这个类是bool类型,非真即假//使用三目运算符 :给Value 赋值为 CheckState//CheckState的状态如果与ECheckBoxState::Checked 的状态相同,就是真,否则是假Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FCustomItemBool::GetCheckBoxState() const
{//上面已经给Value赋值了,这里就问Value是什么?如果是真就是 ECheckBoxState::Checked了,否则。。。return Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
CustomItemInt
#pragma once
#include "CustomItemBase.h"class FCustomItemInt : public FCustomItemBase
{
public:FCustomItemInt(): Value(0){}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;protected:void OnValueChanged(int32 InValue);int32 GetValue() const;
private:int32 Value;
};
#include "CustomItemInt.h"#include "Widgets/Input/SSpinBox.h"TSharedRef<SWidget> FCustomItemInt::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("测试Int32")));
}TSharedRef<SWidget> FCustomItemInt::MakeValueWidget()
{return SNew(SBox).WidthOverride(200).MaxDesiredWidth(400).HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SNew(SSpinBox<int32>).OnValueChanged(this, &FCustomItemInt::OnValueChanged).Value(this, &FCustomItemInt::GetValue)];
}void FCustomItemInt::OnValueChanged(int32 InValue)
{Value = InValue;
}int32 FCustomItemInt::GetValue() const
{return Value;
}
streetview组装类
CustomDetailPlane
#pragma once
#include "CustomItemBase.h"//这里是使用typedef创建别名的方式,用FCustomItemBase中的数据,创建了一个树,
typedef STreeView<TSharedRef<FCustomItemBase>> CustomDetailTreeView;class SCustomDetailPlane : public SCompoundWidget
{SLATE_BEGIN_ARGS(SCustomDetailPlane){}SLATE_END_ARGS()SCustomDetailPlane();virtual ~SCustomDetailPlane();void Construct(const FArguments& InArgs);//树形结构中的行,这里就是一行一列,这里可以再装入1行2列或是其他。。。TSharedRef<ITableRow> CustomOnGenerateRow(TSharedRef<FCustomItemBase> Item,const TSharedRef< STableViewBase >& TableView);//SLATE_EVENT( FOnGetChildren, OnGetChildren ),//事件类型,进一步查看API DECLARE_DELEGATE_TwoParams (FOnGetChildren,ArgumentType, TArray<ArgumentType>& );//TwoParams表示需要两个参数,没有返回值,因为是GetChildren,ArgumentType应该是Children父类,然后从父类获取包含的子类void OnGetChildren(TSharedRef<FCustomItemBase> ParentItem, TArray<TSharedRef<FCustomItemBase>>& OutChildrens);//当CustomItemBool类创建好后,这时已经在类中确定了要做的事,这里创建一个函数用于给TreeItemSources赋值测试void SetItemData();private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;//这里用的是上面创建的别名,生成指向STreeView<TSharedRef<FCustomItemBase>>的指针//因为STreeView<TSharedRef<FCustomItemBase>>太长,所以使用别名,也方便修改TSharedPtr<CustomDetailTreeView> OtherNameDetailTreeView;//树结构需要的数据源,(即已经准备好的价签,展台已经准备好了,需要几层就放入几个价签)TArray<TSharedRef<FCustomItemBase>> TreeItemSources;
};
#include "CustomDetailPlane.h"#include "CustomItemBool.h"
#include "Widgets/Layout/SWidgetSwitcher.h"SCustomDetailPlane::SCustomDetailPlane()
{
}SCustomDetailPlane::~SCustomDetailPlane()
{
}void SCustomDetailPlane::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+ SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("Custom Detail Plane"))]+ SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[//生成树控件,只有树干SAssignNew(OtherNameDetailTreeView, CustomDetailTreeView)//设置数据源(即:准备好树枝).TreeItemsSource(&TreeItemSources)//绑定代理(将生成事件与实际生成方法绑定到一起).OnGenerateRow(this, &SCustomDetailPlane::CustomOnGenerateRow)//绑定代理(将获取事件与实际获取方法绑定到一起).OnGetChildren(this, &SCustomDetailPlane::OnGetChildren)//生成列.HeaderRow(SNew(SHeaderRow)+ SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(20, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString("Name"))]]+ SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0)).FillWidth(0.6f)[SNew(SBorder).Padding(FMargin(20, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString("Value"))]])]];SetItemData();
}TSharedRef<ITableRow> SCustomDetailPlane::CustomOnGenerateRow(TSharedRef<FCustomItemBase> Item,const TSharedRef<STableViewBase>& TableView)
{return SNew(SMultiDetailTableRow, TableView, Item);
}void SCustomDetailPlane::OnGetChildren(TSharedRef<FCustomItemBase> ParentItem,TArray<TSharedRef<FCustomItemBase>>& OutChildrens)
{ParentItem->GetChildrens(OutChildrens);
}void SCustomDetailPlane::SetItemData()
{//相当于实例化了一个FCustomItemBool类型的小控件BoolItem,等待显示TSharedRef<FCustomItemBool> BoolItem = MakeShareable(new FCustomItemBool());//在FCustomItemBool中已经将水果放到了果篮中并将果篮摆在了层板上,这里将层板放到树上TreeItemSources.Add(BoolItem);//这里如果你又创建了新的控件,就还用上面的MakeShareable(new FCustomItemBool());方法//然后用TreeItemSources.Add(BoolItem);添加//刷新OtherNameDetailTreeView->RequestTreeRefresh();//显示WidgetSwitcher的那一页WidgetSwitcher->SetActiveWidgetIndex(1);
}
使用FProperty反射
使用FProperty反射,用widget控件实时控制类中变量
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ActorBase.generated.h"UCLASS()
class CUSTOMWINDOW_API AActorBase : public AActor
{GENERATED_BODY()public:// Sets default values for this actor's propertiesAActorBase();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:// Called every framevirtual void Tick(float DeltaTime) override;protected:UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")bool bIsSelect;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FVector _Location;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FVector _Scale;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FRotator _Rotation;
};
// Fill out your copyright notice in the Description page of Project Settings.#include "ActorBase.h"// Sets default values
AActorBase::AActorBase()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void AActorBase::BeginPlay()
{Super::BeginPlay();}// Called every frame
void AActorBase::Tick(float DeltaTime)
{Super::Tick(DeltaTime);
}
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "ActorBase.h"
#include "TestActor.generated.h"UCLASS()
class CUSTOMWINDOW_API ATestActor : public AActorBase
{GENERATED_BODY()public:// Sets default values for this actor's propertiesATestActor();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:// Called every framevirtual void Tick(float DeltaTime) override;UFUNCTION(BlueprintCallable, Category="TestActor")void SetSelect(bool bInValue);UFUNCTION(BlueprintCallable, Category="TestActor")void SetTestPropertChange(const FString& InValue);UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")int32 TestInt32 = 16;
};
// Fill out your copyright notice in the Description page of Project Settings.#include "TestActor.h"// Sets default values
ATestActor::ATestActor()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void ATestActor::BeginPlay()
{Super::BeginPlay();}// Called every frame
void ATestActor::Tick(float DeltaTime)
{Super::Tick(DeltaTime);GEngine->AddOnScreenDebugMessage(1,2.f,FColor::Orange,FString::Printf(TEXT("bIsSelect = %d TestInt32 = %d"),bIsSelect,TestInt32));
}void ATestActor::SetSelect(bool bInValue)
{
}void ATestActor::SetTestPropertChange(const FString& InValue)
{
}
************
#pragma once
#include "ActorBase.h"// Item
class FTreeViewItemBase : public TSharedFromThis<FTreeViewItemBase>
{
protected:using Super = FTreeViewItemBase;
public:FTreeViewItemBase(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName);virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;virtual ~FTreeViewItemBase(){}void Getchildren(TArray<TSharedRef<FTreeViewItemBase>>& OutChildren) const{OutChildren = Children;}
protected:TArray<TSharedRef<FTreeViewItemBase>> Children;TWeakObjectPtr<AActorBase> ActorBasePtr;FProperty* PropertyPtr = nullptr;
};// Row单行多列
class SMultiTableRow : public SMultiColumnTableRow<TSharedRef<FTreeViewItemBase>>
{SLATE_BEGIN_ARGS(SMultiTableRow){}SLATE_END_ARGS()virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase);
private:TWeakPtr<FTreeViewItemBase> TreeViewItemBasePtr;
};
#include "TreeViewItemBase.h"FTreeViewItemBase::FTreeViewItemBase(TWeakObjectPtr<AActorBase> InActorPtr, const FString& PropertyName): ActorBasePtr(InActorPtr)
{if (ActorBasePtr.IsValid()){PropertyPtr = FindFProperty<FProperty>(ActorBasePtr->GetClass(),*PropertyName);}
}TSharedRef<SWidget> SMultiTableRow::GenerateWidgetForColumn(const FName& ColumnName)
{if (ColumnName.IsEqual((TEXT("Name")))){return TreeViewItemBasePtr.Pin()->MakeNameWidget();}if (ColumnName.IsEqual(TEXT("Value"))){return TreeViewItemBasePtr.Pin()->MakeValueWidget(); }return SNullWidget::NullWidget;
}void SMultiTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView,const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase)
{TreeViewItemBasePtr = InTreeViewItemBase;STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,2,0,2));SMultiColumnTableRow::Construct(ParentArgs, InOwnerTableView);
}
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseBool : public FTreeViewItemBase
{
public:FTreeViewItemBaseBool(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName): Super(InActorPtr,PropertyName){if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(PropertyPtr)){Value = BoolProperty->ContainerPtrToValuePtr<bool>(InActorPtr.Get());}}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool* Value = nullptr;};
#include "TreeViewItemBaseBool.h"TSharedRef<SWidget> FTreeViewItemBaseBool::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FTreeViewItemBaseBool::MakeValueWidget()
{return SNew(SCheckBox).OnCheckStateChanged(this, &FTreeViewItemBaseBool::OnCheckStateChanged).IsChecked(this, &FTreeViewItemBaseBool::GetCheckBoxState);}void FTreeViewItemBaseBool::OnCheckStateChanged(ECheckBoxState CheckState)
{*Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FTreeViewItemBaseBool::GetCheckBoxState() const
{return *Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseNumber : public FTreeViewItemBase
{
public:FTreeViewItemBaseNumber(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName): Super(InActorPtr,PropertyName){if (PropertyPtr){Value = PropertyPtr->ContainerPtrToValuePtr<int32>(InActorPtr.Get());}}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;protected:void OnValueChanged(int32 InValue);int32 GetValue() const;
private:int32* Value = nullptr;
};
#include "TreeViewItemBaseNumber.h"#include "Widgets/Input/SSpinBox.h"TSharedRef<SWidget> FTreeViewItemBaseNumber::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("测试Int32")));
}TSharedRef<SWidget> FTreeViewItemBaseNumber::MakeValueWidget()
{return SNew(SBox).WidthOverride(200).MaxDesiredWidth(400).HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SNew(SSpinBox<int32>).OnValueChanged(this, &FTreeViewItemBaseNumber::OnValueChanged).Value(this, &FTreeViewItemBaseNumber::GetValue)];
}void FTreeViewItemBaseNumber::OnValueChanged(int32 InValue)
{*Value = InValue;
}int32 FTreeViewItemBaseNumber::GetValue() const
{return *Value;
}
*****************************
#pragma once
#include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget
{SLATE_BEGIN_ARGS(SDetailTabWidget){}SLATE_END_ARGS()void Construct(const FArguments& InArgs);private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;TSharedPtr<STreeView<TSharedRef<FTreeViewItemBase>>> TreeViewItem;TArray<TSharedRef<FTreeViewItemBase>> TreeViewSource;public:TSharedRef<class ITableRow> GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase, const TSharedRef< class STableViewBase >& TableViewBase);void OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase, TArray<TSharedRef<FTreeViewItemBase>>& OutChildren);void SetItemData();
};
#include "DetailTabWidget.h"#include "TestActor.h"
#include "TreeViewItemBaseBool.h"
#include "TreeViewItemBaseNumber.h"
#include "Kismet/GameplayStatics.h"
#include "Widgets/Layout/SWidgetSwitcher.h"void SDetailTabWidget::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+ SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("This DetailTab Panel"))]+ SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SAssignNew(TreeViewItem, STreeView<TSharedRef<FTreeViewItemBase>>).TreeItemsSource(&TreeViewSource)//委托,生成每一行,绑定自定义方法(事件),触发时生成.OnGenerateRow(this,&SDetailTabWidget::GenerateTreeViewRow).OnGetChildren(this,&SDetailTabWidget::OnGetChildren).HeaderRow(SNew(SHeaderRow)+ SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0, 0, 0, 0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString(TEXT("名 称")))]]+ SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0, 0, 0, 0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString(TEXT("属 性")))]])]];SetItemData();
}TSharedRef<class ITableRow> SDetailTabWidget::GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase,const TSharedRef<class STableViewBase>& TableViewBase)
{return SNew(SMultiTableRow, TableViewBase, ItemBase);
}void SDetailTabWidget::OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase,TArray<TSharedRef<FTreeViewItemBase>>& OutChildren)
{ItemBase->Getchildren(OutChildren);
}void SDetailTabWidget::SetItemData()
{UWorld* World = GWorld->GetWorld();AActor* FindAcr = UGameplayStatics::GetActorOfClass(World, ATestActor::StaticClass());//这里需要注意TestActor必须放置在场景中,否则运行崩溃 if (AActorBase* ActorBase = Cast<AActorBase>(FindAcr)){TSharedRef<FTreeViewItemBaseBool> BoolItem = MakeShareable(new FTreeViewItemBaseBool(ActorBase,"bIsSelect" ));TreeViewSource.Add(BoolItem);TSharedRef<FTreeViewItemBaseNumber> NumberItem = MakeShareable(new FTreeViewItemBaseNumber(ActorBase,"TestInt32"));TreeViewSource.Add(NumberItem);TreeViewItem->RequestTreeRefresh();WidgetSwitcher->SetActiveWidgetIndex(1);}
}
相关文章:

ue5 创建多列StreeView的方法与理解
创建StreeView的多列样式怎么就像是创建单行单列差不多?貌似就是在单行单列中加入了多列widget? 示例代码 DetailTabWidget #pragma once #include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget {SLATE_BEGIN_ARGS(SDetailT…...

C# OnnxRuntime部署DAMO-YOLO香烟检测
目录 说明 效果 模型信息 项目 代码 下载 参考 说明 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name:input tensor:Floa…...

陕西省地标-DB61/T 1121-2018 政务服务中心建设和运营规范
揭秘陕西省智慧政务服务中心新标准:打造高效便捷的服务新体验 随着信息化时代的深入发展,智慧政务已成为提升政府服务效率、优化营商环境的重要举措。陕西省作为全国政务改革的先行者,近期颁布了《陕西省地标-DB61_T 1121-2018 政务服务中心…...

UDP协议(20250303)
1. UDP UDP:用户数据报协议(User Datagram Protocol),传输层协议之一(UDP,TCP) 2. 特性 发送数据时不需要建立链接,节省资源开销不安全不可靠的协议 //一般用在实时性比较高…...

【四.RAG技术与应用】【12.阿里云百炼应用(下):RAG的云端优化与扩展】
在上一篇文章中,我们聊了如何通过阿里云百炼平台快速搭建一个RAG(检索增强生成)应用,实现文档智能问答、知识库管理等基础能力。今天咱们继续深入,聚焦两个核心问题:如何通过云端技术优化RAG的效果,以及如何扩展RAG的应用边界。文章会穿插实战案例,手把手带你踩坑避雷。…...
Docker新手入门(持续更新中)
一、定义 快速构建、运行、管理应用的工具。 Docker可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用。 所谓镜像,就是将应用所需的函数库、依赖、配置等应用一起打包得到的。 所谓容器,为每个镜像的应用进程创建…...

【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架 1. 引言 本教程旨在帮助嵌入式开发小白从零开始,学习如何在STM32F407微控制器上实现一个基于串口的数据接收程序。该程序能够通过判断数据头来接收一串数据,并将其存储到缓冲区中…...
HSPF 水文模型建模方法与案例分析实践技术应用
在水文模拟领域,HSPF 模型(Hydrological Simulation Program Fortran)与 SWAT 模型一样,都是备受瞩目的水文模型软件。HSPF 模型因其强大的功能和简便的操作,在全球范围内得到了广泛应用。该模型不仅能够在缺乏测量数据…...

设置 CursorRules 规则
为什么要设置CursorRules? 设置 CursorRules 可以帮助优化代码生成和开发流程,提升工作效率。具体的好处包括: 1、自动化代码生成 :通过定义规则,Cursor 可以根据你的开发需求自动生成符合规定的代码模板,…...

人工智能AI在汽车设计领域的应用探索
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活…...

《当AI生成内容遭遇审核:需求与困境的深度剖析》:此文为AI自动生成
AI 内容审核:数字时代的守门人 在当今数字技术迅猛发展的浪潮中,AI 在内容生成领域取得了令人瞩目的成就,成为了推动创新与变革的核心力量。以 AI 绘画为例,从早期简单粗糙的图像生成,到如今能够创作出细节丰富、风格多…...

【无人机与无人车协同避障】
无人机与无人车协同避障的关键在于点云数据的采集、传输、解析及实时应用,以下是技术实现的分步解析: 1. 点云数据采集(无人机端) 传感器选择: LiDAR:通过激光雷达获取高精度3D点云(精度达厘米…...
ComfyUI AnimeDiff动画参数总结
ComfyUI AnimeDiff动画参数总结 一、动画生成核心参数 参数名称建议值/范围作用说明备注步数(Steps)15-25控制AI计算迭代次数,越高细节越精细,但耗时更长推荐20步,显存不足可降至15步CFG值7.0-8.5提示词对画面的控制…...

No manual entry for printf in section 3
问题描述 在尝试查看 printf 的 C 函数手册页(即 man 3 printf)时遇到了 “No manual entry for printf in section 3” 的错误信息。 解决方案 出现这问题,是由于系统上没有安装对应的部分的手册页,因此安装对应的部分的手册…...
React 之 Redux 第二十八节 学习目标与规划大纲及概要讲述
接下来 开始Redux 全面详细的文档输出,主要基于一下几个方面,欢迎大家补充指正 一、Redux 基础概念 为什么需要 Redux? 前端状态管理的挑战(组件间通信、状态共享) Redux 解决的问题:集中式、可预测的状态…...

OSPF路由ISIS路由与路由学习对比(OSPF vs ISIS Routing Learning Comparison)
OSPF路由ISIS路由与路由学习对比 1.OSPF 路由学习规律 OSPF使用链路状态数据库(Link State Database)来存储网络拓扑信息。每个OSPF路由器通过交换链路状态更新(Link State Updates)来了解整个网络的拓扑,并根据收到…...
PMP项目管理—资源管理篇—1.规划资源管理
文章目录 基本信息4W1HITTO输入工具与技术输出 三种组织结构图和职位描述组织分解结构 OBS职责分派矩阵 RAMRACI矩阵说明 文本格式 资源管理计划团队章程 基本信息 4W1H what: 规划资源管理是定义如何估算、获取、管理和利用端对以及实物资源的过程。why: 资源规划用于确定和…...
Kafka 消息 0 丢失的最佳实践
文章目录 Kafka 消息 0 丢失的最佳实践生产者端的最佳实践使用带有回调的 producer.send(msg, callback) 方法设置 acks all设置 retries 为一个较大的值启用幂等性与事务(Kafka 0.11)正确关闭生产者与 flush() 方法 Broker 端的最佳实践设置 unclean.l…...

机器学习算法——回归任务
回归分析是估计因变量和自变量之间关系的过程。 目录 1、多元线性回归 2、岭回归 3、Lasso回归 4、弹性网络回归 5、多项式回归 6、指数回归 7、自然对数回归 8、广义线性模型 GLM 9、Cox比例风险模型 10、决策树回归 11、随机森林回归 12、梯度提升回归 13、XGBoost回归 14、…...

【数据库】数据库基础
第一章 数据库基础 一、数据库基础1.1 数据库系统的体系结构 (三层模式两级映像)1.1.1 逻辑模式1.1.2 外模式1.1.3 内模式1.1.4 外模式/模式映象1.1.5 逻辑模式/内模式映象1.1.6 逻辑独立性1.1.7 物理独立性 1.2 数据模型 一、数据库基础 1.1 数据库系统…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...