UE4笔记-系统对话框相关功能记录和使用方法(打开/保存文件对话框系统对话框等等...)
(当前最新版本4.24)
UE4其实是有实现对话框打开保存等的相关的功能的,都在‘DesktopPlatform’ Module中..
但是‘DesktopPlatform’ Module属于Developer模块,是无法打包出ship ping程序包的...
会报错:
所以想在生产环境中使用该模块需要自己整改一下Module的(突然想口吐芬芳~)
------------------------------------------------------------------------------------------------
补全:如果仅考虑windows平台的话且没有UE风格统一强迫症的话可以直接调用Windows 的API进行封装
e.g:
#include "Engine.h" #include "AllowWindowsPlatformTypes.h" #include "Windows/COMPointer.h" #include <shlobj.h> #include "HideWindowsPlatformTypes.h" UENUM(BlueprintType) enum class ESystemFileDialogType : uint8 { Save, Open }; bool UGenericExtensionMethods::OpenSystemFileDialog(FString& OutFilePath, const FString& Title, const FString& DefaultPath, ESystemFileDialogType Type, const FString& Filter) { OutFilePath.Empty(); TComPtr<IFileDialog> FileDialog; CLSID type_clsid; IID type_iid; switch( Type) { case ESystemFileDialogType::Open: { type_clsid = CLSID_FileOpenDialog; type_iid = IID_IFileOpenDialog; break; } case ESystemFileDialogType::Save: { type_clsid = CLSID_FileSaveDialog; type_iid = IID_IFileSaveDialog; break; } } //auto c = CLSID_FileSaveDialog; if (!SUCCEEDED(::CoCreateInstance( type_clsid, nullptr, CLSCTX_INPROC_SERVER, type_iid, IID_PPV_ARGS_Helper(&FileDialog) ))) { return false; } // Set up common settings FileDialog->SetTitle( *Title); FString DefaultWindowsPath = DefaultPath; if (DefaultWindowsPath.IsEmpty() ) { DefaultWindowsPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); } DefaultWindowsPath.ReplaceInline(TEXT("/"), TEXT("\\"), ESearchCase::CaseSensitive); TComPtr<IShellItem> DefaultPathItem; if (SUCCEEDED(::SHCreateItemFromParsingName(*DefaultWindowsPath, nullptr, IID_PPV_ARGS(&DefaultPathItem)))) { FileDialog->SetFolder(DefaultPathItem); } TArray<FString> UnformattedExtensions; TArray<COMDLG_FILTERSPEC> FileDialogFilters; { Filter.ParseIntoArray(UnformattedExtensions, TEXT("|"), true); if (UnformattedExtensions.Num() % 2 == 0) { FileDialogFilters.Reserve(UnformattedExtensions.Num() / 2); for (int32 ExtensionIndex = 0; ExtensionIndex < UnformattedExtensions.Num();) { COMDLG_FILTERSPEC& NewFilterSpec = FileDialogFilters[FileDialogFilters.AddDefaulted()]; NewFilterSpec.pszName = *UnformattedExtensions[ExtensionIndex++]; NewFilterSpec.pszSpec = *UnformattedExtensions[ExtensionIndex++]; } } } FileDialog->SetFileTypes(FileDialogFilters.Num(), FileDialogFilters.GetData()); // show if (!SUCCEEDED( FileDialog->Show(static_cast<HWND>(FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()-> GetOSWindowHandle())))) { return false; } TComPtr<IShellItem> Result; if (!SUCCEEDED(FileDialog->GetResult(&Result))) { return false; } PWSTR pFilePath = nullptr; if (SUCCEEDED(Result->GetDisplayName(SIGDN_FILESYSPATH, &pFilePath))) { FString SaveFilePath = pFilePath; int32 FilterIndex = 0; if (SUCCEEDED(FileDialog->GetFileTypeIndex(reinterpret_cast<UINT*>(&FilterIndex)))) { FilterIndex -= 1; // GetFileTypeIndex returns a 1-based index } if (FileDialogFilters.IsValidIndex(FilterIndex)) { const FString ExtensionPattern = FileDialogFilters[FilterIndex].pszSpec; TArray<FString> SaveExtensions; ExtensionPattern.ParseIntoArray(SaveExtensions, TEXT(";")); FString CleanExtension = SaveExtensions[0]; if (CleanExtension == TEXT("*.*")) { CleanExtension.Reset(); } else { int32 WildCardIndex = INDEX_NONE; if (CleanExtension.FindChar(TEXT('*'), WildCardIndex)) { CleanExtension = CleanExtension.RightChop(WildCardIndex + 1); } } FString SaveFileName = FPaths::GetCleanFilename(SaveFilePath); SaveFilePath = FPaths::GetPath(SaveFilePath); if (FPaths::GetExtension(SaveFileName).IsEmpty() && !CleanExtension.IsEmpty()) { SaveFileName = FPaths::SetExtension(SaveFileName, CleanExtension); } OutFilePath = FPaths::ConvertRelativePathToFull(FPaths::Combine(SaveFilePath, SaveFileName)); } } ::CoTaskMemFree(pFilePath); return true; }
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
整理步骤记录:
在Engine\Source\Developer 找到DesktopPlatform和SlateFileDialogs(DesktopPlatform的依赖,也是属于Developer)文件夹..
然后将其复制粘贴到自己Project(插件里也可以)的Source中..
再修改掉DesktopPlatform和SlateFileDialogs的Module名称并builds装载即可(防止模块重名,不好区分),例如改成DesktopPlatformEx和SlateFileDialogsEx
需要注意一下Build.cs 的PrivateIncludePaths路径
------------------------------------------------------------------------------------------------
DesktopPlatform模块的简单使用示例(打开文件对话框):
FString UCommonBPLibrary::OpenFileDialog() { void* ParentWindowPtr = FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle(); IDesktopPlatform* DesktopPlatform = FDesktopPlatformModuleEx::Get(); FJsonSerializableArray OutFileNames; if (DesktopPlatform) { EFileDialogFlags::Type SelectionFlag = EFileDialogFlags::None; //A value of 0 represents single file selection while a value of 1 represents multiple file selection DesktopPlatform->OpenFileDialog( ParentWindowPtr, TEXT( "选取一张图片" ), TEXT( "/" ), TEXT( "" ), TEXT( "(Image Files)|*.BMP;*.JPG;*.PNG;*.JPEG;)" ), SelectionFlag , OutFileNames ); } if (OutFileNames.Num() > 0) { return FString (OutFileNames[0]); } else { return TEXT(""); } } TArray<FString> UCommonBPLibrary::OpenFileDialogMultiple() { TArray<FString> ret_arr; void* ParentWindowPtr = FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle(); IDesktopPlatform* DesktopPlatform = FDesktopPlatformModuleEx::Get(); FJsonSerializableArray OutFileNames; if (DesktopPlatform) { EFileDialogFlags::Type SelectionFlag = EFileDialogFlags::Multiple; //A value of 0 represents single file selection while a value of 1 represents multiple file selection DesktopPlatform->OpenFileDialog(ParentWindowPtr, TEXT("选取图片"), TEXT("/"), TEXT(""), TEXT("(Image Files)|*.BMP;*.JPG;*.PNG;*.JPEG;)"), SelectionFlag, OutFileNames); } for (int i = 0; i < OutFileNames.Num(); i++) { ret_arr.Add(FString(OutFileNames[i])); } return ret_arr; }