Qt打开文件夹并选中文件

QDesktopServices::openUrl() 如果参数是文件夹会在资源管理器打开,如果参数是文件会用默认打开方式打开,不符合需求。想要打开文件夹并选中文件,在 Windows 上可以用 QProcess 执行 explorer 命令:

QString command = "explorer.exe";
QStringList arguments;
const QFileInfo file_info(file_path);
if (!file_info.isDir()) {
    arguments << QLatin1String("/select,");
}
arguments << QDir::toNativeSeparators(file_info.canonicalFilePath());

QProcess::startDetached(command, arguments);

但是现在有两个问题:

1.第一次执行可能只会打开文件夹,再次执行才会选中文件(测试环境 Win10);

2.想要支持 Mac 系统。

这时候就需要参考下其他的开源代码,比如 Qt Creator 5 的源码:

//qt-creator-opensource-src-5.0.0\src\plugins\coreplugin\fileutils.cpp
void FileUtils::showInGraphicalShell(QWidget *parent, const QString &pathIn)
{
    const QFileInfo fileInfo(pathIn);
    // Mac, Windows support folder or file.
    if (HostOsInfo::isWindowsHost()) {
        const FilePath explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
        if (explorer.isEmpty()) {
            QMessageBox::warning(parent,
                                 QApplication::translate("Core::Internal",
                                                         "Launching Windows Explorer Failed"),
                                 QApplication::translate("Core::Internal",
                                                         "Could not find explorer.exe in path to launch Windows Explorer."));
            return;
        }
        QStringList param;
        if (!fileInfo.isDir())
            param += QLatin1String("/select,");
        param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
        QProcess::startDetached(explorer.toString(), param);
    } else if (HostOsInfo::isMacHost()) {
        QStringList scriptArgs;
        scriptArgs << QLatin1String("-e")
                   << QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
                                         .arg(fileInfo.canonicalFilePath());
        QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
        scriptArgs.clear();
        scriptArgs << QLatin1String("-e")
                   << QLatin1String("tell application \"Finder\" to activate");
        QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
    } else {
        // we cannot select a file here, because no file browser really supports it...
        const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
        const QString app = UnixUtils::fileBrowser(ICore::settings());
        QStringList browserArgs = ProcessArgs::splitArgs(
                    UnixUtils::substituteFileBrowserParameters(app, folder));
        QString error;
        if (browserArgs.isEmpty()) {
            error = QApplication::translate("Core::Internal",
                                            "The command for file browser is not set.");
        } else {
            QProcess browserProc;
            browserProc.setProgram(browserArgs.takeFirst());
            browserProc.setArguments(browserArgs);
            const bool success = browserProc.startDetached();
            error = QString::fromLocal8Bit(browserProc.readAllStandardError());
            if (!success && error.isEmpty())
                error = QApplication::translate("Core::Internal",
                                                "Error while starting file browser.");
        }
        if (!error.isEmpty())
            showGraphicalShellError(parent, app, error);
    }
}

在 Qt Creator 9 中 Mac 下的命令变了:

void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn)
{
    // ... ...
    } else if (HostOsInfo::isMacHost()) {
        QtcProcess::startDetached({"/usr/bin/open", {"-R", fileInfo.canonicalFilePath()}});
    } else {
    // ... ...
}

bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid)
{
    return QProcess::startDetached(cmd.executable().toUserOutput(),
                                   cmd.splitArguments(),
                                   workingDirectory.toUserOutput(),
                                   pid);
}

很显然,他在 Windows 上也是用的 explorer,所以第一次打开也会有问题。其次,Qt Creator 虽然支持 Mac,但是 Qt Creator 9 右键打开文件位置有时候非常慢(测试环境 Mac 10.14)。

然后我又找到 Wireshark 的源码也有这个功能,和 Qt Creator 5 差不多,使用 osascript 在 Mac 上打开 Finder 速度是正常的。具体哪个版本的代码合适还需要进行更多的测试。

// https://github.com/wireshark/wireshark
void desktop_show_in_folder(const QString &file_path)
{
    bool success = false;
    // https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt

#if defined(Q_OS_WIN)
    QString command = "explorer.exe";
    QStringList arguments;
    QString path = QDir::toNativeSeparators(file_path);
    arguments << "/select," << path + "";
    success = QProcess::startDetached(command, arguments);
#elif defined(Q_OS_MAC)
    QStringList script_args;
    QString escaped_path = file_path;

    escaped_path.replace('"', "\\\"");
    script_args << "-e"
               << QString("tell application \"Finder\" to reveal POSIX file \"%1\"")
                                     .arg(escaped_path);
    if (QProcess::execute("/usr/bin/osascript", script_args) == 0) {
        success = true;
        script_args.clear();
        script_args << "-e"
                   << "tell application \"Finder\" to activate";
        QProcess::execute("/usr/bin/osascript", script_args);
    }
#else
    // Is there a way to highlight the file using xdg-open?
#endif
    if (!success) {
        QFileInfo file_info(file_path);
        QDesktopServices::openUrl(QUrl::fromLocalFile(file_info.dir().absolutePath()));
    }
}

解决了 Mac 版的功能,回头又去找 Windows 打开文件夹并选中文件的的方案,发现 COM 接口的效果还不错,始终能打开并选中:

#include <Windows.h>
#include <objbase.h>
#pragma comment(lib, "Ole32.lib")
#include <ShlObj_core.h>
#pragma comment(lib, "Shell32.lib")

void win_show_in_folder(const QString &file_path)
{
    ::CoInitialize(nullptr);
    QString path = QDir::toNativeSeparators(file_path);
    std::wstring str = path.toStdWString();
    PIDLIST_ABSOLUTE pidl = ::ILCreateFromPathW((PCWSTR)str.c_str());
    if(pidl){
        ::SHOpenFolderAndSelectItems(pidl,0,0,0);
        ::ILFree(pidl);
    }
    ::CoUninitialize();
}

猜你喜欢

转载自blog.csdn.net/gongjianbo1992/article/details/129233861