当前位置: 首页 > article >正文

UE 树形图 C++版

1.创建以下三个C类(1)UTreeViewItemBase(基类UObject)UCLASS() class UTreeViewItemBase : public UObject { GENERATED_BODY() public: UTreeViewItemBase(); virtual ~UTreeViewItemBase() override; public: FString Id; //层级 int32 Level; //标签 FText Label; //与自身对应的Actor AActor* SelfActor; //SelfActor 的初始材质 TArraylt;UMaterialInterface*gt;OriginalMaterials; //子级Item数组 TArraylt;UObject*gt; ChildrenData; }该类型为树形图三要素中的Item①Level:决定Item对应Entry的在树形图中的层级结构,如下图1-1②Label:Entry显示的文本③SelfActor: Actor-Item-Entry. Item是Actor和Entry之间的桥梁,如下图1-2④ChildrenData:存储本级Item的子级Item对象(2)UEntryWidgetBase(基类UUserWidget,实现接口IUserObjectListEntry)UCLASS() class UEntryWidgetBase : public UUserWidget,public IUserObjectListEntry { GENERATED_BODY() public: //override begin virtual void NativeConstruct() override; virtual void NativePreConstruct() override; virtual void NativeOnInitialized() override; //override end public: //子级entry缩进像素长度 UPROPERTY(EditAnywhere,BlueprintReadOnly,Categoryquot;EntryWidgetBasequot;) float SubItemIndentWidth20.f; UCheckBox* CheckBox; UTextBlock* TextBlock; UHorizontalBox* HorBox; UTreeView* TreeView; protected: //Entry对应的Item UTreeViewItemBase* ListItemObject; }该类型为树形图三要素中的Entry①CheckBox,TextBlock,HorBox控件,如下图1-3②ListItemObject:Entry对应的Item(3)UTreeViewPanelBase(基类UUserWidget)UCLASS() class UTreeViewPanelBase : public UUserWidget { GENERATED_BODY() public: UTreeViewPanelBase(const FObjectInitializeramp; ObjectInitializer); //override begin virtual void NativeConstruct() override; virtual void NativePreConstruct() override; virtual bool Initialize() override; //override end UTreeView* TreeView; }该类型为树形图三要素中的TreeView2.成员函数说明(1)UTreeViewItemBase类中定义以下成员函数public: //初始化子级 void InitChildren(); /** * brief 设置子级隐藏 * param bHidden true:隐藏;false:显示 */ void SetChildrenHiddenInGame(bool bHidden); /** * brief 缓存对应的模型的初始材质 */ void CacheOriginalMaterials(); /** * brief 被选中状态,材质更换 */ void OnSelected(); /** * brief 取消选中状态,材质恢复 */ void UnSelected(); /** * brief 根据选中的Actor找到其对应的Item * param SelectedActor 选中的Actor * param OutItem 对应的item */ void FindSubItemByGivenActor(AActor* SelectedActor,UObject*amp;OutItem);解析:①InitChildrenvoid UTreeViewItemBase::InitChildren() { UnSelected(); //缓存初始材质 CacheOriginalMaterials(); //获得所有子级actor TArraylt;AActor*gt;AttachedActors; SelfActor-gt;GetAttachedActors(AttachedActors); ChildrenData.Reset(); //为所有子级actor创建对应的子级item for(int i0;ilt;AttachedActors.Num();i) { if (!AttachedActors[i]) continue; UTreeViewItemBase* ItemNewObjectlt;UTreeViewItemBasegt;(this); Item-gt;IdFString::Printf(TEXT(quot;%s_%dquot;),*Id,i); Item-gt;LevelLevel1; Item-gt;LabelFText::FromString(UKismetSystemLibrary::GetDisplayName(AttachedActors[i])); Item-gt;SelfActorAttachedActors[i]; Item-gt;InitChildren(); ChildrenData.Add(Item); } }该成员函数用于查找SelfActor存在多少个子级Actor,然后为每个子级创建子级Item,并将子级Item与子级Actor建立对应关系.ps:初始化子级时要注意,子级可能也有子级,所以需要通过递归逐级往下初始化②SetChildrenHiddenInGamevoid UTreeViewItemBase::SetChildrenHiddenInGame(bool bHidden) { //设置对应actor的显隐性 SelfActor-gt;SetActorHiddenInGame(bHidden); //递归设置子级显隐性 for (UObject*amp; ChildObject:ChildrenData) { UTreeViewItemBase* ChildItemCastlt;UTreeViewItemBasegt;(ChildObject); if (ChildItem) { ChildItem-gt;SetChildrenHiddenInGame(bHidden); } } }该成员函数用于控制子级Actor(包含自身)的显隐性,如2-1③CacheOriginalMaterialsvoid UTreeViewItemBase::CacheOriginalMaterials() { //获得static mesh 组件 UStaticMeshComponent* StaticMeshCompCastlt;UStaticMeshComponentgt;(SelfActor-gt;GetComponentByClass(UStaticMeshComponent::StaticClass())); if (StaticMeshComp amp;amp; StaticMeshComp-gt;GetStaticMesh()) { TArraylt;UMaterialInterface*gt;Materials; MaterialsStaticMeshComp-gt;GetMaterials(); OriginalMaterialsMaterials; } }该函数用于缓存SelfActor的初始材质④OnSelectedvoid UTreeViewItemBase::OnSelected() { //获得静态网格组件 UStaticMeshComponent* SMCCastlt;UStaticMeshComponentgt;(SelfActor-gt;GetComponentByClass(UStaticMeshComponent::StaticClass())); if(!SMC) return; //下面注释部分是从配置文件中读取高亮材质,本篇教程不对配置文件进行赘述,可自行加载一个材质用于实现选中高亮效果,如LoadObject /****************************** //获得树形图工具配置文件 UTreeViewToolSettings* TreeViewToolSettingsUTreeViewToolSettings::GetTreeViewToolSettings(); //获得配置文件里配置的高亮材质 TSoftObjectPtrlt;UMaterialInterfacegt; MaterialTreeViewToolSettings-gt;HightLightMaterial.LoadSynchronous(); if (!Material.IsValid()) return; ***********************************************/ //将所有材质设置为高亮 int MaterialNumSMC-gt;GetNumMaterials(); for (int i0;ilt;MaterialNum;i) { UMaterialInstanceDynamic* DMISMC-gt;CreateDynamicMaterialInstance(i,Material.Get()); DMI-gt;SetVectorParameterValue(FName(quot;HightLightColorquot;),TreeViewToolSettings-gt;Color); } }该函数实现了点击Entry,使其对应的Actor高亮(被选中效果) ,效果如参考图2-1⑤UnSelectedvoid UTreeViewItemBase::UnSelected() { //获得static mesh组件 UStaticMeshComponent* SMCCastlt;UStaticMeshComponentgt;(SelfActor-gt;GetComponentByClass(UStaticMeshComponent::StaticClass())); if(!SMC) return; //重置材质为原始材质 for (int i0;ilt;OriginalMaterials.Num();i) { SMC-gt;SetMaterial(i,OriginalMaterials[i]); } for(UObject* Child:ChildrenData) { if(UTreeViewItemBase* ChildItemCastlt;UTreeViewItemBasegt;(Child)) { //递归取消选中状态 ChildItem-gt;UnSelected(); } } }该函数实现了取消选中效果,使其对应的Actor由选中高亮效果恢复到初始材质⑥FindSubItemByGivenActorvoid UTreeViewItemBase::FindSubItemByGivenActor(AActor* SelectedActor, UObject*amp; OutItem) { if(SelfActorSelectedActor) { OutItemthis; return; } for (UObject*amp;ChildObj:ChildrenData) { UTreeViewItemBase*ChildItemCastlt;UTreeViewItemBasegt;(ChildObj); ChildItem-gt;FindSubItemByGivenActor(SelectedActor,OutItem); } }该函数用于查找与给定Actor对应的Item,用于点击Actor反向定位树形图中的Entry,如下图(2)UEntryWidgetBaseNativeOnInitialized(初始化函数)实现void UEntryWidgetBase::NativeOnInitialized() { Super::NativeOnInitialized(); //获得umg 控件 TextBlockCastlt;UTextBlockgt;(GetWidgetFromName(FName(quot;Textblockquot;))); CheckBoxCastlt;UCheckBoxgt;(GetWidgetFromName(FName(quot;CheckBoxquot;))); HorBoxCastlt;UHorizontalBoxgt;(GetWidgetFromName(FName(quot;HorBoxquot;))); //绑定checkbox勾选状态改变回调 if(CheckBox) { CheckBox-gt;OnCheckStateChanged.AddDynamic(this,amp;UEntryWidgetBase::OnCheckBoxStateChanged); } //默认勾选状态 CheckBox-gt;SetCheckedState(ECheckBoxState::Checked); }类中定义以下成员函数public: /** * brief 推测Entry的勾选状态 * param Item entry对应的Item * return 勾选状态 */ ECheckBoxState PredictEntryState(UTreeViewItemBase* Item); /** * brief 递归刷新entry的上一级entry状态 */ void RefreshParentEntries(); /** * brief 递归刷新子级entry状态 * param State 子级状态受父级entry checkbox控件选中状态影响 */ void RefreshChildrenEntries(ECheckBoxState State); /** * brief 刷新各级Entry状态 * param State checkbox 控件选中状态 */ void RefreshHierarchyState(ECheckBoxState State); /** * brief checkbox控件选中状态更改回调函数 * param bIsChecked 是否勾选 */ UFUNCTION() void OnCheckBoxStateChanged(bool bIsChecked); /** * brief 根据entry选中状态切换字体 * param bIsSelected 选中:粗体;未选中:正常字体 */ void SwitchFontBySelectState(bool bIsSelected); /** * brief 根据entry选中状态更新对应模型的高亮效果 * param bIsSelected 选中高亮,未选中恢复正常 */ void SwitchMaterialBySelectState(bool bIsSelected); protected: //IUserObjectListEntry begin void NativeOnListItemObjectSet(UObject* ListItemObject) override; void NativeOnItemSelectionChanged(bool bIsSelected) override; void NativeOnEntryReleased() override; //IUserObjectListEntry end解析:①PredictEntryState(推测Entry状态)ECheckBoxState UEntryWidgetBase::PredictEntryState(UTreeViewItemBase* Item) { ECheckBoxState StateECheckBoxState::Checked; //注1: int Count0; if (!Item) return State; //统计子级Entry勾选checkbox的数量 if (Item-gt;ChildrenData.Num()gt;0) { for (UObject*amp; ChildData:Item-gt;ChildrenData) { UEntryWidgetBase* ChildEntryCastlt;UEntryWidgetBasegt;(TreeView-gt;GetEntryWidgetFromItem(ChildData));: if (ChildEntry) { ECheckBoxState ChildStateChildEntry-gt;CheckBox-gt;GetCheckedState(); switch (ChildState) { case ECheckBoxState::Checked: Count; break; case ECheckBoxState::Undetermined: Count10000; break; default: break; } } //entry无效(不显示时)以对应item的actor显隐性为准 else { UTreeViewItemBase* ChildItemCastlt;UTreeViewItemBasegt;(ChildData); if (ChildItem) CountChildItem-gt;SelfActor-gt;IsHidden()?0:1; } } //注2 StateItem-gt;ChildrenData.Num()Count?ECheckBoxState::Checked:ECheckBoxState::Undetermined; } //没有子级时,以对应item的actor的显隐性为准,显示checked;隐藏unchecked else { StateItem-gt;SelfActor-gt;IsHidden()?ECheckBoxState::Unchecked:ECheckBoxState::Checked; } return State; }注1:父级的勾选状态(checkbox)受子级影响.通过对比子级已勾选数量对比子级数量来决定父级的勾选状态.注2:子级Entry全部Checked(子级数量Count):父级Checked子级Entry部分Checked 或者 全部UnChecked(子级数量!Count):父级Undetermined参考如下图2-22-2②RefreshParentEntries(刷新父级Entry状态)void UEntryWidgetBase::RefreshParentEntries() { if(!TreeView) return; //获得父级item和entry UTreeViewItemBase* ParentItemCastlt;UTreeViewItemBasegt;(ListItemObject-gt;GetOuter()); UEntryWidgetBase* ParentEntryCastlt;UEntryWidgetBasegt;(TreeView-gt;GetEntryWidgetFromItem(ParentItem)); if (ParentEntry) { //推测父级checkbox状态 ECheckBoxState ParentStatePredictEntryState(ParentItem); ParentEntry-gt;CheckBox-gt;SetCheckedState(ParentState); //递归刷新更上一级父级 ParentEntry-gt;RefreshParentEntries(); } }向上递归刷新父级Entry的状态,效果参考2-2③RefreshChildrenEntries(刷新子级Entry状态)void UEntryWidgetBase::RefreshChildrenEntries(ECheckBoxState State) { if(!TreeView) return; //刷新子级checkbox和启用状态 for(UObject*amp; ChildData:ListItemObject-gt;ChildrenData) { UEntryWidgetBase* EntryTreeView-gt;GetEntryWidgetFromItemlt;UEntryWidgetBasegt;(ChildData); //entry有效时设置勾选状态 if (Entry) { Entry-gt;CheckBox-gt;SetCheckedState(State); Entry-gt;RefreshChildrenEntries(State); } } }遍历递归更新本级Entry的子级Entry(以及子级的子级)的状态.效果参考2-2④RefreshHierarchyState(刷新层级结构状态,即子父级一块刷新)void UEntryWidgetBase::RefreshHierarchyState(ECheckBoxState State) { if(StateECheckBoxState::Undetermined) return; //根据勾选状态更新对应的Actor的显隐性 ListItemObject-gt;SetChildrenHiddenInGame(CheckBox-gt;GetCheckedState()ECheckBoxState::Unchecked); RefreshChildrenEntries(State); RefreshParentEntries(); }效果参考2-2④OnCheckBoxStateChanged(Checkbox勾选状态改变回调)void UEntryWidgetBase::OnCheckBoxStateChanged(bool bIsChecked) { RefreshHierarchyState(bIsChecked?ECheckBoxState::Checked:ECheckBoxState::Unchecked); }该回调函数在本篇开头NativeOnInitialized中绑定.2-3⑤SwitchFontBySelectState(选中切换字体)void UEntryWidgetBase::SwitchFontBySelectState(bool bIsSelected) { //此处字体选择逻辑仅为示例,实际使用建议将字体公开到蓝图,可在蓝图中进行手动选择 UFont* FontObjectnullptr; if(bIsSelected) { FontObjectLoadObjectlt;UFontgt;(nullptr,TEXT(quot;/Engine/EngineFonts/BoldFontquot;)); } else { FontObjectLoadObjectlt;UFontgt;(nullptr,TEXT(quot;/Engine/EngineFonts/Robotoquot;)); } //设置字体 if(FontObject) { FSlateFontInfo FontInfoTextBlock-gt;GetFont(); FontInfo.FontObjectFontObject; TextBlock-gt;SetFont(FontInfo); } }效果参考2-3⑥SwitchMaterialBySelectState(选中高亮)void UEntryWidgetBase::SwitchMaterialBySelectState(bool bIsSelected) { //根据选中状态更换材质 if (bIsSelected) { ListItemObject-gt;OnSelected(); } else { ListItemObject-gt;UnSelected(); } }效果参考2-3⑦NativeOnListItemObjectSetvoid UEntryWidgetBase::NativeOnListItemObjectSet(UObject* ItemObject) { //获取TreeView TreeViewCastlt;UTreeViewgt;(GetOwningListView()); //获得对应的Item ListItemObjectCastlt;UTreeViewItemBasegt;(ItemObject); //设置标签字体,文本内容和字体大小,字体大小与level挂钩,级别越高字体越大 TextBlock-gt;SetText(ListItemObject-gt;Label); FSlateFontInfo FontInfoTextBlock-gt;GetFont(); FontInfo.SizeUKismetMathLibrary::MapRangeClamped(ListItemObject-gt;Level,1,6,15,8); TextBlock-gt;SetFont(FontInfo); //根据Entry的level调整文本缩进长度 UBorderSlot* BorderSlotCastlt;UBorderSlotgt;(HorBox-gt;Slot); if (BorderSlot) { BorderSlot-gt;SetPadding(FMargin(ListItemObject-gt;Level*SubItemIndentWidth,0,0,0)); } CheckBox-gt;SetCheckedState(PredictEntryState(ListItemObject)); RefreshParentEntries(); }该接口函数触发时会返回Entry对应的Item,根据Item的成员变量Level(层级结构的级别,级树形图中的第n级)来决定Entry的字体大小以及文本缩进长度,如下图2-42-4⑧NativeOnItemSelectionChangedvoid UEntryWidgetBase::NativeOnItemSelectionChanged(bool bIsSelected) { //根据选中状态更换字体和材质 SwitchFontBySelectState(bIsSelected); if(bIsSelected) { ListItemObject-gt;OnSelected(); } else { ListItemObject-gt;UnSelected(); } }该接口函数在Entry的选中状态改变时触发,效果参考2-3⑨NativeOnEntryReleasedvoid UEntryWidgetBase::NativeOnEntryReleased() { SwitchFontBySelectState(false); SwitchMaterialBySelectState(false); }该接口在Entry被释放的时候触发,将字体和Actor材质还原.Entry在以下情况会被释放:1.子级Entry被折叠收起时会触发Release,此外,被展开时会触发NativeOnListItemObjectSet2.Entry不被绘制时,即在视口中不可见,如随着scrollbox滑动导致看不见,同理,当Entry重新被绘制时,触发NativeOnListItemObjectSet两种情况效果如下图2-52-5(3)UTreeViewPanelBaseNativeConstruct函数实现void UTreeViewPanelBase::NativeConstruct() { Super::NativeConstruct(); TreeViewCastlt;UTreeViewgt;(GetWidgetFromName(FName(quot;TreeViewquot;))); //绑定回调函数 TreeView-gt;SetOnGetItemChildrenlt;UTreeViewPanelBasegt;(this,amp;UTreeViewPanelBase::OnGetItemChildren); }类中定义以下成员函数/** * brief 查找actor的父级 * param Actor 要被查找父级的actor * param bFindTopLevelParent 是否查找最上级父级 */ void FindParentActor(AActor*amp; GivenActor,bool bFindTopLevelParenttrue); UFUNCTION(BlueprintCallable,Categoryquot;TreeViewPanelquot;) void StartSelect(); /** * brief 选中一个actor用于显示其层级结构 * param Level 层级级别 * param Actor 选中的Actor * param bUseTopLevelParent 是否使用选中actor的最上级父类作为层级结构起点 */ UFUNCTION(BlueprintCallable,Categoryquot;TreeViewPanelquot;) void OnSelectActor(const int32 Level,AActor* Actor,bool bUseTopLevelParenttrue); /** * brief 展开所有Item */ UFUNCTION(BlueprintCallable,Categoryquot;TreeViewPanelquot;) void ExpandAllItems(); /** * brief * param Item 展开单个Item */ void ExpandItem(UObject* Item); /** * brief 收起所有Item */ UFUNCTION(BlueprintCallable,Categoryquot;TreeViewPanelquot;) void CollapseAllItem(); /** * brief 刷新树形图 */ void RefreshTreeView(); /** * brief 获得item children回调函数 * param Item * param Children */ UFUNCTION() void OnGetItemChildren(UObject* Item,TArraylt;UObject*gt;amp; Children);解析:①FindParentActor3-1void UTreeViewPanelBase::FindParentActor(AActor*amp; GivenActor, bool bFindTopLevelParent) { if(!Acotr) return; //获得父级actor AActor* ParentActorAcotr-gt;GetAttachParentActor(); if (ParentActor) { AcotrParentActor; //递归继续查找更上层父级 if (bFindTopLevelParent) { FindParentActor(Acotr,bFindTopLevelParent); } } }GivenActor:选中的Actor(例如被鼠标点击到的)bFindTopLevelParent:true表示查找层级结构中的最上级,如传入参考图3-1中的Cone,返回Cubefalse表示查找结构中的上级,如传入Cone返回Sphere②StartSelect(选取场景中Actor)void UTreeViewPanelBase::StartSelect() { APlayerController* PlayerControllerGetWorld()-gt;GetFirstPlayerController(); if(PlayerController-gt;IsInputKeyDown(EKeys::RightMouseButton)) return; FVector Location,Direction; if(PlayerController-gt;DeprojectMousePositionToWorld(Location,Direction)) { FHitResult HitResult; bool bHitUKismetSystemLibrary::LineTraceSingle( this, Location, Direction*100000.fLocation, ETraceTypeQuery::TraceTypeQuery1, true,TArraylt;AActor*gt;(), EDrawDebugTrace::ForOneFrame, HitResult, true); if (bHit) { OnSelectActor(1,HitResult.GetActor(),true); } } }③OnSelectActor(选中Actor)void UTreeViewPanelBase::OnSelectActor(const int32 Level, AActor* Actor, bool bUseTopLevelParent) { //查找模型最上级的层级 AActor*ParentActorActor; FindParentActor(ParentActor,bUseTopLevelParent); if (!ParentActor) return; bool bNeedRebuildTreeViewtrue; if(TreeView-gt;GetListItems().IsValidIndex(0)) { UTreeViewItemBase* RootItemCastlt;UTreeViewItemBasegt;(TreeView-gt;GetListItems()[0]); //注1 if(RootItem-gt;SelfActorParentActor) { bNeedRebuildTreeViewfalse; UObject* OutItemnullptr; RootItem-gt;FindSubItemByGivenActor(Actor,OutItem); if (OutItem) { TArraylt;UUserWidget*gt; EntryWidgetsTreeView-gt;GetDisplayedEntryWidgets(); for (UUserWidget*amp; EntryWidget:EntryWidgets) { UEntryWidgetBase* EntryCastlt;UEntryWidgetBasegt;(EntryWidget); UObject* ItemObjEntry-gt;GetListItemlt;UObjectgt;(); //注2 TreeView-gt;SetItemSelection(ItemObj,false); } //注3 TreeView-gt;RequestNavigateToItem(OutItem); TreeView-gt;SetItemSelection(OutItem,true); } } } //重新构建树形图 if (bNeedRebuildTreeView) { UE_LOG(LogTemp,Warning,TEXT(quot;Select a new object :%squot;),*ParentActor-gt;GetName()); UTreeViewItemBase* ItemNewObjectlt;UTreeViewItemBasegt;(this); Item-gt;Idquot;0quot;; Item-gt;LevelLevel; Item-gt;LabelFText::FromString(UKismetSystemLibrary::GetDisplayName(ParentActor)); Item-gt;SelfActorParentActor; Item-gt;InitChildren(); TreeView-gt;ClearListItems(); TreeView-gt;AddItem(Item); } //展开所有item ExpandAllItems(); }注1:判断选中的Actor的父级Actor是否是上次选中的(即是否切换目标),如下图3-23-2注2:在未切换目标的情况下,将未被选中的子Entry设为未选择状态(未高亮)注3:将选中的Entry设为已选中(高亮)效果如下图3-33-3④ExpandItem(展开单个) ExpandAllItems(展开所有Item)引擎提供的ExpandAll并不能真正的展开所有的层级结构3-4调用引擎提供的ExpandAll仅能展开了一层结构,效果如3-53-5为此我们需要自行实现ExpandAll功能,代码如下void UTreeViewPanelBase::ExpandItem(UObject* Item) { TreeView-gt;SetItemExpansion(Item,true); UTreeViewItemBase* TreeViewItemCastlt;UTreeViewItemBasegt;(Item); for (UObject*amp; ChildItem:TreeViewItem-gt;ChildrenData) { if(ChildItem) { //递归展开当前Item的Children ExpandItem(ChildItem); } } } void UTreeViewPanelBase::ExpandAllItems() { //官方提供的expandall节点智能展开一级entry,再深层子级无法展开 TreeView-gt;ExpandAll(); //手动展开item TArraylt;UObject*gt; ItemsTreeView-gt;GetListItems(); if (Items.IsValidIndex(0)) { ExpandItem(Items[0]); } }⑤CollapseAllItem(折叠收起所有Item)void UTreeViewPanelBase::CollapseAllItem() { TreeView-gt;CollapseAll(); }⑥RefreshTreeView(刷新层级结构)void UTreeViewPanelBase::RefreshTreeView() { if(TreeView-gt;GetListItems().IsValidIndex(0)) { UTreeViewItemBase* RootItemCastlt;UTreeViewItemBasegt;(TreeView-gt;GetListItems()[0]); RootItem-gt;InitChildren(); TreeView-gt;ClearListItems(); TreeView-gt;AddItem(RootItem); ExpandAllItems(); } }注1:Treeview通过GetListItems获得的Item数组中,第0个元素表示根Item注2:刷新时清空原有的Item注3:默认刷新时展开所有层级,可根据自身需求选择,不想展开就是执行ExpandAllItems⑦OnGetItemChildrenvoid UTreeViewPanelBase::OnGetItemChildren(UObject* Item,TArraylt;UObject*gt;amp; Children) { UTreeViewItemBase* TreeViewItemCastlt;UTreeViewItemBasegt;(Item); if(!TreeViewItem) return; ChildrenTreeViewItem-gt;ChildrenData; }该函数在Treeview执行AddItem时自动触发.一.创建UMG1.WBP_EntryWidget1-12.WBP_TreeViewPanel1-2代码如下1-3解析:(1)使用右键鼠标进行选择目标(2)EventOnMouseEnter和EventIOnMouseLeave 事件为了防止鼠标穿透UI选中目标二.演示至此树形图功能讲解完毕!

相关文章:

UE 树形图 C++版

1.创建以下三个C类(1)UTreeViewItemBase(基类UObject)UCLASS() class UTreeViewItemBase : public UObject {GENERATED_BODY() public:UTreeViewItemBase();virtual ~UTreeViewItemBase() override; public:FString Id;//层级int32 Level;//标签FText Label;//与自身对应的Acto…...

UE 树形图(Tree View)_1

一.树形图介绍1.所需类型(1)TreeView控件:作为树形图主体,后面简称树形图(2)EntryWidget:本质是一个UserWidget,作为树形图中的子元素存在,必须要实现UserObjectListEntry接口,否则无法作为树形图的子元素.后面简称Entry(3)TreeViewItem:EntryWidget对应的信息or实体,基类为UOb…...

华为OD机试双机位C卷-生成哈夫曼树 (Py/Java/C/C++/Js/Go)

生成哈夫曼树 华为OD机试双机位C卷 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 给定长度为 n 的无序的数字数组,每个数字代表二叉树的叶子节点的权值,数字数组的值均大…...

Java毕业设计基于springboot的智能推荐旅游平台95722270

前言 在个性化消费时代背景下,旅游市场的需求日益多样化和个性化。传统的旅游推荐方式已经难以满足游客对个性化、精准化服务的需求。因此,开发一个基于Spring Boot的智能推荐旅游平台具有重要意义。该平台能够综合分析游客的旅行偏好、时间安排、预算限…...

Java毕业设计基于springboot+Java株洲崔氏服装有限公司服装生产管理信息系统

前言 随着株洲崔氏服装有限公司业务的不断发展,传统的生产管理方式已难以满足高效、精准的管理需求。因此,公司决定采用现代信息技术,设计与实现一个服装生产管理信息系统,以实现对服装生产全过程的信息化管理,提高生产…...

JAVA EE(进阶)_CSS

人与人之间总是渐渐疏离 ——陳長生. ❀主页:陳長生.-CSDN博客❀ 📕上一篇:JAVA EE(进阶)_HTML-CSDN博客 1.CSS 1.1.介绍 如果说HTML是前端的骨架,那么CSS就是前端的外表 可以做到…...

Java毕业设计基于springboot的某炼油厂盲板管理系统

前言 在炼油厂的生产过程中,盲板的管理是一个至关重要的环节。传统的盲板管理方式往往依赖于人工记录和跟踪,这种方式不仅效率低下,而且容易出错。为了提高盲板管理的效率和安全性,炼油厂引入了基于Spring Boot的盲板管理系统。该…...

如何快速上手Motion UI:从安装到第一个动画效果的简单教程

如何快速上手Motion UI:从安装到第一个动画效果的简单教程 【免费下载链接】motion-ui 💎 The powerful Sass library for creating CSS transitions and animations 项目地址: https://gitcode.com/gh_mirrors/mo/motion-ui Motion UI是一个功能…...

GoMLX核心组件解析:从张量操作到神经网络层

GoMLX核心组件解析:从张量操作到神经网络层 【免费下载链接】gomlx GoMLX -- Accelerated ML Libraries for Go 项目地址: https://gitcode.com/gh_mirrors/go/gomlx GoMLX是Go语言生态中一款强大的加速机器学习库,它提供了从底层张量操作到高层神…...

double-conversion深度解析:从V8引擎到高效IEEE浮点数转换库

double-conversion深度解析:从V8引擎到高效IEEE浮点数转换库 【免费下载链接】double-conversion Efficient binary-decimal and decimal-binary conversion routines for IEEE doubles. 项目地址: https://gitcode.com/gh_mirrors/do/double-conversion dou…...

TDL vs DSEFix:哪种Windows驱动签名绕过工具更适合你?深度对比分析

TDL vs DSEFix:哪种Windows驱动签名绕过工具更适合你?深度对比分析 【免费下载链接】TDL Driver loader for bypassing Windows x64 Driver Signature Enforcement 项目地址: https://gitcode.com/gh_mirrors/tdl1/TDL 在Windows系统开发中&#…...

为什么选择Torch?Elixir开发者必备的5大理由与优势分析

为什么选择Torch?Elixir开发者必备的5大理由与优势分析 【免费下载链接】torch A rapid admin generator for Elixir & Phoenix 项目地址: https://gitcode.com/gh_mirrors/tor/torch Torch是Elixir和Phoenix框架生态中一款强大的快速管理后台生成工具&a…...

【WIN开发】04 四种文件操作之注册表编程

一、什么是注册表? 注册表就是Windows系统的档案室! 按 Win + R 键 输入 regedit 回车即可打开注册表编辑器二、注册表的结构 2.1 五大根键(就像五个大柜子) 根键 缩写 存放什么 HKEY_CLASSES_ROOT HKCR 文件类型、扩展名关联 HKEY_CURRENT_USER HKCU 当前用户的设置 HK…...

华为OD机试双机位C卷-出错的或电路 (C/C++/Py/Java/Js/Go)

出错的或电路 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 华为OD机试双机位C卷 华为OD上机考试双机位C卷 100分题型 题目描述 某生产门电路的厂商发现某一批次的或门电路不稳定,具体现象为计算两个二进制数的或操作时…...

JAVA EE初阶 2: 多线程-初阶

1.认识线程(Thread)1.1 概念1.2 创建线程(1) 方法1 - 继承Thread类【 Ctrl C 取消 终止 】(2) 方法2 - 实现Runnable接口(3) 总结(4) 匿名内部类&#xff0…...

python --飞浆离线ocr使用/paddleocr

python 3.10.3 和 飞浆ocr 3.2使用 依赖 paddlepaddle3.2.0 paddleocr3.2.0运行测试 paddleocr ocr -i https://paddle-model-ecology.bj.bcebos.com/paddlex/imgs/demo_image/general_ocr_002.png --use_doc_orientation_classify False --use_doc_unwarping False --use_text…...

稳转细胞株构建全攻略:从理论到实践的生物制药核心技术解析

为何稳转细胞株成为生物制药领域的“兵家必争之地”?稳转细胞株被称为“药物研发的基石”,是单克隆抗体、双抗、疫苗、ADC等生物制品规模化生产的核心环节,还是药物生产成本、效率和质量的关键决定因素。一株高产稳定的细胞株有可能直接缩短药…...

PHing配置文件完全手册:build.xml编写指南与示例

PHing配置文件完全手册:build.xml编写指南与示例 【免费下载链接】phing PHing Is Not GNU make; its a PHP project build system or build tool based on Apache Ant. 项目地址: https://gitcode.com/gh_mirrors/ph/phing PHing是一款基于Apache Ant的PHP项…...

laravel-menu性能优化:高效生成与缓存策略

laravel-menu性能优化:高效生成与缓存策略 【免费下载链接】laravel-menu A quick way to create menus in Laravel 项目地址: https://gitcode.com/gh_mirrors/la/laravel-menu laravel-menu是一款专为Laravel框架设计的菜单生成工具,能够帮助开…...

2026高职物联网毕业起薪一般多少?就业方向、岗位薪资与升职路径全解析

【先说结论】2026年高职物联网专业毕业生的起薪,通常会落在【4500元—8000元/月】这个区间。若在新一线、强二线城市,且具备项目经验、证书、实习经历,起点拿到【7000元以上】并不罕见;如果是制造业城市、传统中小企业&#xff0c…...

MobileCoin智能合约开发入门:构建基于隐私支付的去中心化应用

MobileCoin智能合约开发入门:构建基于隐私支付的去中心化应用 【免费下载链接】mobilecoin Private payments for mobile devices. 项目地址: https://gitcode.com/gh_mirrors/mo/mobilecoin MobileCoin是一个专注于移动设备隐私支付的开源项目,它…...

2026大专财富管理毕业工资多少?就业薪资、岗位方向与升职考证全攻略

【先说结论】2026年大专学财富管理,毕业后的起薪通常不会“一步到位特别高”,但它是一个【越做越值钱】的专业。若进入银行、证券、保险、第三方财富公司等行业,刚毕业月薪大多在【4500元—9000元】之间;如果在一线城市、业绩岗、…...

2026年低功耗国产32位MCU哪家强?业内权威评测榜单揭晓

在物联网、智能家居、可穿戴设备以及工业传感器等终端应用爆发的当下,低功耗已成为衡量微控制器(MCU)核心竞争力的关键指标之一。对于工程师和采购决策者而言,如何在琳琅满目的国产芯片中选择一款兼具“低功耗”与“高性能”的32位…...

Obsidian Importer常见问题解答:解决90%用户都会遇到的导入难题

Obsidian Importer常见问题解答:解决90%用户都会遇到的导入难题 【免费下载链接】obsidian-importer Obsidian Importer lets you import notes from other apps and file formats into your Obsidian vault. 项目地址: https://gitcode.com/gh_mirrors/ob/obsidi…...

安全优先!2026年电机微控制器 MCU 精选推荐

在工业自动化、汽车电子以及智能家居领域,电机作为核心执行单元,其控制系统的安全性与可靠性直接关系到整机设备的稳定运行与使用寿命。随着工业4.0和新能源汽车的快速发展,市场对电机微控制器(MCU)的要求已不再局限于…...

MoonShine Admin Panel完全解析:为何它是2024年最值得尝试的Laravel后台框架?

MoonShine Admin Panel完全解析:为何它是2024年最值得尝试的Laravel后台框架? 【免费下载链接】moonshine Simple for beginners and powerful for experts 项目地址: https://gitcode.com/gh_mirrors/moonsh/moonshine MoonShine Admin Panel是一…...

eslint-plugin-jest核心功能解析:为什么它是Jest测试的最佳拍档

eslint-plugin-jest核心功能解析:为什么它是Jest测试的最佳拍档 【免费下载链接】eslint-plugin-jest ESLint plugin for Jest 项目地址: https://gitcode.com/gh_mirrors/es/eslint-plugin-jest eslint-plugin-jest是专为Jest测试框架打造的ESLint插件&…...

为什么VoxFormer能减少45% GPU内存占用?揭秘高效内存管理策略

为什么VoxFormer能减少45% GPU内存占用?揭秘高效内存管理策略 【免费下载链接】VoxFormer Official PyTorch implementation of VoxFormer [CVPR 2023 Highlight] 项目地址: https://gitcode.com/gh_mirrors/vo/VoxFormer VoxFormer作为CVPR 2023 Highlight的…...

jeston orin nx系统问题汇总

1、浏览器打不开 way1:遇到浏览器打不开的问题,snap兼容性问题,可以用上面这个指令处理sudo snap revert snapd way2:https://blog.csdn.net/Flag_ing/article/details/150655043 2、摄像头导致存储爆满 step1:立即释放磁盘空间 不要直接…...

ScrewFast模板入门:如何使用这款开源Astro框架快速搭建现代网站

ScrewFast模板入门:如何使用这款开源Astro框架快速搭建现代网站 【免费下载链接】ScrewFast Open-source Astro website template with sleek, customizable TailwindCSS components. 项目地址: https://gitcode.com/gh_mirrors/sc/ScrewFast ScrewFast是一款…...