UE 树形图 C++版

news2026/3/17 16:24:01
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选中目标二.演示至此树形图功能讲解完毕!

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…