让Qt 具有多选文件夹和记忆上一次打开位置的文件对话框

最近要做一个可以多选文件夹的功能,在网上查阅了多个资料,发现github有一段代码可以实现该功能,于是将其收入进行改造。另外qt自带的 getExistingDirectory 和 getOpenFileNames 不具有记忆上一次打开的文件夹位置。要实现多选文件夹和记忆上一次打开位置能用到的就只有IFileOpenDialog 接口了。
原代码如下
出处:https://gist.github.com/0xF5T9/3f3203950f480d348aa6d99850a26016

#include <iostream>
#include <windows.h>
#include <shobjidl.h>
#include <string>
#include <vector>/*** @brief Open a dialog to select item(s) or folder(s).* @param paths Specifies the reference to the string vector that will receive the file or folder path(s). [IN]* @param selectFolder Specifies whether to select folder(s) rather than file(s). (optional)* @param multiSelect Specifies whether to allow the user to select multiple items. (optional)* @note If no item(s) were selected, the function still returns true, and the given vector is unmodified.* @note `<windows.h>`, `<string>`, `<vector>`, `<shobjidl.h>`* @return Returns true if all the operations are successfully performed, false otherwise.*/
bool OpenFileDialog(std::vector<std::wstring> &paths, bool selectFolder = false, bool multiSelect = false)
{IFileOpenDialog *p_file_open = nullptr;bool are_all_operation_success = false;while (!are_all_operation_success){HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,IID_IFileOpenDialog, reinterpret_cast<void **>(&p_file_open));if (FAILED(hr))break;if (selectFolder || multiSelect){FILEOPENDIALOGOPTIONS options = 0;hr = p_file_open->GetOptions(&options);if (FAILED(hr))break;if (selectFolder)options |= FOS_PICKFOLDERS;if (multiSelect)options |= FOS_ALLOWMULTISELECT;hr = p_file_open->SetOptions(options);if (FAILED(hr))break;}hr = p_file_open->Show(NULL);if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No items were selected.{are_all_operation_success = true;break;}else if (FAILED(hr))break;IShellItemArray *p_items;hr = p_file_open->GetResults(&p_items);if (FAILED(hr))break;DWORD total_items = 0;hr = p_items->GetCount(&total_items);if (FAILED(hr))break;for (int i = 0; i < static_cast<int>(total_items); ++i){IShellItem *p_item;p_items->GetItemAt(i, &p_item);if (SUCCEEDED(hr)){PWSTR path;hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &path);if (SUCCEEDED(hr)){paths.push_back(path);CoTaskMemFree(path);}p_item->Release();}}p_items->Release();are_all_operation_success = true;}if (p_file_open)p_file_open->Release();return are_all_operation_success;
}/*** @brief Open a dialog to save an item.* @param path Specifies the reference to the string that will receive the target save path. [IN]* @param defaultFileName Specifies the default save file name. (optional)* @param pFilterInfo Specifies the pointer to the pair that contains filter information. (optional)* @note If no path was selected, the function still returns true, and the given string is unmodified.* @note `<windows.h>`, `<string>`, `<vector>`, `<shobjidl.h>`* @return Returns true if all the operations are successfully performed, false otherwise.*/
bool SaveFileDialog(std::wstring &path, std::wstring defaultFileName = L"", std::pair<COMDLG_FILTERSPEC *, int> *pFilterInfo = nullptr)
{IFileSaveDialog *p_file_save = nullptr;bool are_all_operation_success = false;while (!are_all_operation_success){HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL,IID_IFileSaveDialog, reinterpret_cast<void **>(&p_file_save));if (FAILED(hr))break;if (!pFilterInfo){COMDLG_FILTERSPEC save_filter[1];save_filter[0].pszName = L"All files";save_filter[0].pszSpec = L"*.*";hr = p_file_save->SetFileTypes(1, save_filter);if (FAILED(hr))break;hr = p_file_save->SetFileTypeIndex(1);if (FAILED(hr))break;}else{hr = p_file_save->SetFileTypes(pFilterInfo->second, pFilterInfo->first);if (FAILED(hr))break;hr = p_file_save->SetFileTypeIndex(1);if (FAILED(hr))break;}if (!defaultFileName.empty()){hr = p_file_save->SetFileName(defaultFileName.c_str());if (FAILED(hr))break;}hr = p_file_save->Show(NULL);if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No item was selected.{are_all_operation_success = true;break;}else if (FAILED(hr))break;IShellItem *p_item;hr = p_file_save->GetResult(&p_item);if (FAILED(hr))break;PWSTR item_path;hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &item_path);if (FAILED(hr))break;path = item_path;CoTaskMemFree(item_path);p_item->Release();are_all_operation_success = true;}if (p_file_save)p_file_save->Release();return are_all_operation_success;
}int main()
{HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);if (FAILED(hr)){std::cout << "Failed to initialize COM library.\n";return -1;}// Select an example.std::cout << "1. Select an item.\n";std::cout << "2. Select a folder.\n";std::cout << "3. Select multiple items.\n";std::cout << "4. Save an item.\n";std::cout << "5. Save an item with filters.\n";std::cout << "Select an example: ";int choice = 0;std::cin >> choice;switch (choice){// Example: Select an item.case 1:{std::vector<std::wstring> paths;if (OpenFileDialog(paths)){if (!paths.empty()){std::cout << "Total items: " << paths.size() << "\n";for (int i = 0; i < static_cast<int>(paths.size()); ++i)std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n";}elsestd::cout << "No item were selected.\n";}break;}// Example: Select a folder.case 2:{std::vector<std::wstring> paths;if (OpenFileDialog(paths, true)){if (!paths.empty()){std::cout << "Total items: " << paths.size() << "\n";for (int i = 0; i < static_cast<int>(paths.size()); ++i)std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n";}elsestd::cout << "No item were selected.\n";}break;}// Example: Select multiple items.case 3:{std::vector<std::wstring> paths;if (OpenFileDialog(paths, false, true)){if (!paths.empty()){std::cout << "Total items: " << paths.size() << "\n";for (int i = 0; i < static_cast<int>(paths.size()); ++i)std::wcout << L"Path " << std::to_wstring(i + 1) << L": " << paths[i] << L"\n";}elsestd::cout << "No item were selected.\n";}break;}// Example: Save an item.case 4:{std::wstring path = L"";if (SaveFileDialog(path, L"Some file.txt")){if (!path.empty()){std::wcout << L"Selected save path:  " << path << L"\n";}elsestd::cout << "No item were selected.\n";}break;}// Example: Save an item with filters.case 5:{std::wstring path = L"";const unsigned int total_filters = 3;COMDLG_FILTERSPEC filters[total_filters];filters[0].pszName = L"All files. (*.*)";filters[0].pszSpec = L"*.*";filters[1].pszName = L"Image files. (.bmp, .jpg, .png)";filters[1].pszSpec = L"*.bmp;*.jpg;*.png";filters[2].pszName = L"Specific file. (unique_file.txt)";filters[2].pszSpec = L"unique_file.txt";std::pair<COMDLG_FILTERSPEC *, int> filter_info = std::make_pair<COMDLG_FILTERSPEC *, int>(filters, total_filters);if (SaveFileDialog(path, L"", &filter_info)){if (!path.empty()){std::wcout << L"Selected save path: " << path << L"\n";}elsestd::cout << "No item were selected.\n";}break;}}CoUninitialize();return 0;
}std::vector<std::pair<std::wstring, std::wstring>> filters = {{L"文件类型(*.txt)", L"*.txt"},        // 过滤 .txt 文件};		if (auto files = GetOpenFileNames(L"导入转换的TXT文件", true, filters);files.has_value()){for (const auto& filename : files.value()){...}}

于是将其中的OpenFileDialog函数拿来进行改造:分别是选择文件 GetOpenFileNames 和文件夹 GetExistingDirectorys

使用时需要加入以下几个头文件:

#include <windows.h>
#include <shobjidl.h>
#include <string>
#include <vector>
#include<optional>

GetOpenFileNames 函数定义如下:

std::optional<std::vector <std::wstring>> GetOpenFileNames(const std::wstring& dialogTitle, bool multiSelect, const std::vector<std::pair<std::wstring, std::wstring>>& filters)
{IFileOpenDialog* p_file_open = nullptr;bool are_all_operation_success = false;std::optional<std::vector <std::wstring>>files;while (!are_all_operation_success){// Create the file dialog instanceHRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,IID_IFileOpenDialog, reinterpret_cast<void**>(&p_file_open));if (FAILED(hr))break;// Set dialog title if specifiedif (!dialogTitle.empty()){hr = p_file_open->SetTitle(dialogTitle.c_str());if (FAILED(hr))break;}// Handle folder selection and multi-select optionsif ( multiSelect){FILEOPENDIALOGOPTIONS options = 0;hr = p_file_open->GetOptions(&options);if (FAILED(hr))break;					options |= FOS_ALLOWMULTISELECT;hr = p_file_open->SetOptions(options);if (FAILED(hr))break;}if (!filters.empty()){std::vector<COMDLG_FILTERSPEC> filterSpecs;for (const auto& filter : filters){COMDLG_FILTERSPEC spec;spec.pszName = filter.first.c_str();  // Filter name as LPCWSTRspec.pszSpec = filter.second.c_str(); // File spec (e.g. "*.txt") as LPCWSTRfilterSpecs.push_back(spec);}// Now, we correctly call SetFileTypes to set the filterhr = p_file_open->SetFileTypes(static_cast<UINT>(filterSpecs.size()), filterSpecs.data());if (FAILED(hr)){// If it failed, we break the loopbreak;}}// Show the dialoghr = p_file_open->Show(NULL);if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No items were selected.{are_all_operation_success = true;break;}else if (FAILED(hr))break;// Retrieve the selected itemsIShellItemArray* p_items;hr = p_file_open->GetResults(&p_items);if (FAILED(hr))break;DWORD total_items = 0;hr = p_items->GetCount(&total_items);if (FAILED(hr))break;// Iterate over the selected items and add their paths to the vectorfor (int i = 0; i < static_cast<int>(total_items); ++i){IShellItem* p_item;p_items->GetItemAt(i, &p_item);if (SUCCEEDED(hr)){PWSTR path;hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &path);if (SUCCEEDED(hr)){if(!files.has_value()){files.emplace();}files->emplace_back(path);				CoTaskMemFree(path);}p_item->Release();}}p_items->Release();are_all_operation_success = true;}if (p_file_open)p_file_open->Release();CoUninitialize();return  files ;
}

GetExistingDirectorys函数定义如下:

std::optional<std::vector<std::wstring>> GetExistingDirectorys(const std::wstring& dialogTitle, bool multiSelect)
{IFileOpenDialog* p_file_open = nullptr;bool are_all_operation_success = false;std::optional<std::vector<std::wstring>> paths;while (!are_all_operation_success){// Create the file dialog instanceHRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,IID_IFileOpenDialog, reinterpret_cast<void**>(&p_file_open));if (FAILED(hr))break;// Set dialog title if specifiedif (!dialogTitle.empty()){hr = p_file_open->SetTitle(dialogTitle.c_str());if (FAILED(hr))break;}// Handle folder selection and multi-select optionsif ( multiSelect){FILEOPENDIALOGOPTIONS options = 0;hr = p_file_open->GetOptions(&options);if (FAILED(hr))break;			options |= FOS_ALLOWMULTISELECT|FOS_PICKFOLDERS;hr = p_file_open->SetOptions(options);if (FAILED(hr))break;}	// Show the dialoghr = p_file_open->Show(NULL);if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) // No items were selected.{are_all_operation_success = true;break;}else if (FAILED(hr))break;// Retrieve the selected itemsIShellItemArray* p_items;hr = p_file_open->GetResults(&p_items);if (FAILED(hr))break;DWORD total_items = 0;hr = p_items->GetCount(&total_items);if (FAILED(hr))break;// Iterate over the selected items and add their paths to the vectorfor (int i = 0; i < static_cast<int>(total_items); ++i){IShellItem* p_item;p_items->GetItemAt(i, &p_item);if (SUCCEEDED(hr)){PWSTR path;hr = p_item->GetDisplayName(SIGDN_FILESYSPATH, &path);if (SUCCEEDED(hr)){if(!paths.has_value()){paths.emplace();}paths->emplace_back(path);CoTaskMemFree(path);}p_item->Release();}}p_items->Release();are_all_operation_success = true;}if (p_file_open)p_file_open->Release();CoUninitialize();return paths;}//使用例子:
std::wstring dialogTitle = L"选择资料的文件夹";	
if (auto selectedPaths = GetExistingDirectorys(dialogTitle, true); selectedPaths.has_value())
{for (const auto& dir : selectedPaths.value()){...}				
}	

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

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

相关文章

【FlutterDart】 拖动边界线改变列宽类似 vscode 那种拖动改变编辑框窗口大小(11 /100)

【Flutter&Dart】 拖动改变 widget 的窗口尺寸大小GestureDetector&#xff5e;简单实现&#xff08;10 /100&#xff09; 【Flutter&Dart】 拖动边界线改变列宽并且有边界高亮和鼠标效果&#xff08;12 /100&#xff09; 上效果&#xff1a; 这个在知乎里找到的效果&…

【Rust自学】11.1. 编写和运行测试

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.1.1. 什么是测试 在Rust里一个测试就是一个函数&#xff0c;它被用于验证非测试代码的功能是否和预期一致。 在一个测试的函数体里通…

数据分析思维(八):分析方法——RFM分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景 实现效果 该案例实现了使用Three.js创建一个带有聚光灯和旋转立方体的3D场景。 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; THREE.WebGLRenderer 是 Three.js 中用于将场景渲染为 WebGL 内容的核…

Idea-离线安装SonarLint插件地址

地址&#xff1a; SonarQube for IDE - IntelliJ IDEs Plugin | Marketplace 选择Install Plugin from Disk..&#xff0c;选中下载好的插件&#xff0c;然后重启idea

Unity:删除注册表内的项目记录

然后WinR按键输入regedit 打开注册表 在注册表 HKEY CURRENT USER—>SOFTWARE—>Unity—>UnityEditor—>DefaultCompany —>language_Test 中&#xff0c;删除我们的之前存储的语言环境数据。在 “ 三、文本调用和替换 ” 测试时已经将语言环境存储到注册表中了…

JAVA学习记录3

文章为个人学习记录&#xff0c;仅供参考&#xff0c;如有错误请指出。 上期说到使用记事本编写Java程序太过繁琐&#xff0c;所以我们后面都将使用IDEA进行代码的编写、编译和运行。 如何下载安装IDEA&#xff1f; 这个的下载途径也很多&#xff0c;我还是推荐去官网下载(h…

CSS——22.静态伪类(伪类是选择不同元素状态)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>静态伪类</title> </head><body><a href"#">我爱学习</a></body> </html>单击链接前的样式 左键单击&#xff08;且…

IDEA中Maven依赖包导入失败报红的潜在原因

在上网试了别人的八个问题总结之后依然没有解决&#xff1a; IDEA中Maven依赖包导入失败报红问题总结最有效8种解决方案_idea导入依赖还是报红-CSDN博客https://blog.csdn.net/qq_43705131/article/details/106165960 江郎才尽之后突然想到一个原因&#xff1a;<dep…

GMDH自组织网络模型时间序列预测,可预测未来

GMDH自组织网络模型时间序列预测&#xff0c;可预测未来 目录 GMDH自组织网络模型时间序列预测&#xff0c;可预测未来效果一览基本介绍模型构建程序设计学习总结参考资料 效果一览 基本介绍 GMDH自组织网络模型是自组织数据挖掘中的一种模型方法&#xff0c;是基于计算机科学和…

【docker系列】可视化Docker 管理工具——Portainer

1. 介绍 Portainer是一个可视化的Docker操作界面&#xff0c;提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作&#xff08;包括上传下载镜像&#xff0c;创建容器等操作&#xff09;、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录…

Linux/Ubuntu/银河麒麟 arm64 飞腾FT2000 下使用 arm64版本 linuxdeployqt 打包Qt程序

文章目录 一、前言二、环境三、准备1、下载Linuxdeployqt源码2、下载Appimagetool-aarch64.AppImage四、编译linuxdeployqt1.配置环境变量2.编译linuxdeployqt五、安装patchelf六、配置Appimagetool七、打包Qt程序重要提示:测试启动应用八、其他九、最后一、前言 因为项目需要…

pg数据库运维经验2024

这篇文章主要是讲pg运维常见问题&#xff0c;两三年见一次的疑难杂症就不说了。 主要是技术性运维总结&#xff0c;主打通俗易懂和快速上手&#xff0c;尽量避免源码层面等深入分析。 SQL性能与执行计划 执行计划突变 pg官方不支持hint功能&#xff0c;并且计划永远不支持&…

Hadoop 实战笔记(一) -- Windows 安装 Hadoop 3.x

环境准备 安装 JAVA 1.8 Java环境搭建之JDK下载及安装下载 Hadoop 3.3.5 安装包 Hadoop 下载&#xff1a;https://archive.apache.org/dist/hadoop/common/ 一、JAVA JDK 环境检查 二、Hadoop(HDFS)环境搭建 1. 解压安装文件 hadoop-3.3.5.tar 2. 配置环境变量 HADOOP_HO…

个人博客搭建(二)—Typora+PicGo+OSS

个人博客站—运维鹿: http://www.kervin24.top CSDN博客—做个超努力的小奚&#xff1a; 做个超努力的小奚-CSDN博客 一、前言 博客搭建完一直没有更新&#xff0c;因为WordPress自带的文档编辑器不方便&#xff0c;以前用CSDN写作的时候&#xff0c;习惯了Typora。最近对比了…

【向量数据库】搜索算法

最近几年&#xff0c;一种叫做向量数据库的产品&#xff0c;正趁着AI的热潮开始崭露头角。伴随着AI时代的到来&#xff0c;向量将成为一种重要的数据形式&#xff0c;而传统数据库并不适合用来存储和检索向量数据&#xff0c;因此我们大约需要一种专门设计的数据库来处理这些问…

ARM CCA机密计算安全模型之安全生命周期管理

安全之安全(security)博客目录导读 目录 一、固件启用的调试 二、CCA系统安全生命周期 三、重新供应 四、可信子系统与CCA HES 启用 CCA&#xff08;机密计算架构&#xff09;的安全系统是指 CCA 平台的实现处于可信状态。 由于多种原因&#xff0c;CCA 启用系统可能处于不…

k8s排错集:zk集群的pod报错 Init:CrashLoopBackOff无法启动

zk三节点集群&#xff0c;zk-0无法启动 statefulset 进到该node节点上查看容器的报错日志&#xff0c;发现在初始化container的时候一个命令有问题 查看正常zk集群的pod的资源配置文件 解决办法&#xff1a; 修改资源配置文件 应该修改为 chown -R 1000:1000 /zkenv kubec…

Golang的并发编程框架比较

# Golang的并发编程框架比较 中的并发编程 在现代软件开发中&#xff0c;处理高并发的能力愈发重要。Golang作为一门支持并发编程的编程语言&#xff0c;提供了丰富的并发编程框架和工具&#xff0c;使得开发者能够更轻松地处理并发任务。本文将介绍Golang中几种常用的并发编程…

【Web】软件系统安全赛CachedVisitor——记一次二开工具的经历

明天开始考试周&#xff0c;百无聊赖开了一把CTF&#xff0c;还顺带体验了下二开工具&#xff0c;让无聊的Z3很开心&#x1f642; CachedVisitor这题 大概描述一下&#xff1a;从main.lua加载一段visit.script中被##LUA_START##(.-)##LUA_END##包裹的lua代码 main.lua loca…