如何使用Qt实现“在Finder中显示”或“在Explorer中显示”功能

62

是否有可能以跨平台的方式打开Windows资源管理器/OS X Finder中的文件夹并选择/高亮一个文件?目前,我的做法是类似于

QDesktopServices::openUrl( QUrl::fromLocalFile( path ) );

其中path是我要打开的文件夹的完整路径。显然,这只会打开文件夹,我必须手动跟踪需要的文件。当文件夹中有成千上万个文件时,这是一个问题。

如果我将其设置为该文件夹中特定文件的路径,则该文件将使用该MIME类型的默认应用程序打开,并且这不是我所需要的。相反,我需要等同于“在Finder中显示”或“在Explorer中显示”的功能。


澄清一下:您的意思是像单击“在Finder中显示”或“在资源管理器中显示”菜单项时吗? - Austin Hyde
@Austin 是的,我的意思就是类似于“在Finder中显示”或者“在Explorer中显示”。 - nnc
我已经将这个功能从Qt Creator中分离出来,以实现简单的工具化,自动创建SO示例代码的截图 :) [https://github.com/KubaO/stackoverflown/blob/master/tooling/showinshell.cpp] - Kuba hasn't forgotten Monica
6个回答

50

Qt Creator源代码)具有此功能,复制它非常容易:

void FileUtils::showInGraphicalShell(QWidget *parent, const QString &pathIn)
{
    const QFileInfo fileInfo(pathIn);
    // Mac, Windows support folder or file.
    if (HostOsInfo::isWindowsHost()) {
        const FileName 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());
        QProcess browserProc;
        const QString browserArgs = UnixUtils::substituteFileBrowserParameters(app, folder);
        bool success = browserProc.startDetached(browserArgs);
        const QString error = QString::fromLocal8Bit(browserProc.readAllStandardError());
        success = success && error.isEmpty();
        if (!success)
            showGraphicalShellError(parent, app, error);
    }
}

另一篇相关的博客文章(代码更简单,但我没有尝试过所以无法评论)是这个

编辑:

当在Windows上的pathIn中包含空格时,原始代码存在一个错误。 QProcess::startDetached将自动引用包含空格的参数。然而,Windows Explorer不会识别用引号括起来的参数,并且会打开默认位置。在Windows命令行中自己尝试吧:

echo. > "C:\a file with space.txt"
:: The following works
C:\Windows\explorer.exe /select,C:\a file with space.txt  
:: The following does not work
C:\Windows\explorer.exe "/select,C:\a file with space.txt"

因此,

QProcess::startDetached(explorer, QStringList(param));

被改变成

QString command = explorer + " " + param;
QProcess::startDetached(command);

1
我测试了你链接的博客文章中的代码。它确实可以工作(至少在Mac OS X上)。 - Sylvain Defresne
1
@Michael Scheper:我将DanDennedy的答案移植到了PyQt5(请参见此处)。我还没有在Linux上进行测试,但是它非常简单易懂。 - normanius
1
你的编辑是不正确的。至少在Windows 10上,资源管理器可以很好地接受带引号的路径。关键是资源管理器参数必须用逗号分隔,因此当使用/select,(带逗号)而不是/select时,原始源代码是正确的。Qt将根据需要正确地用引号括起路径。来源:http://www.geoffchappell.com/studies/windows/shell/explorer/cmdline.htm - Phlucious
2
发布GPL代码并建议复制粘贴?这是个陷阱! - user2366975
1
它非常关注空格部分,即使在Qt 5.15.0中也是如此。QProcess::startDetached("explorer.exe", {"/select,c:\\space dir\\some.file"});无法工作;QProcess::startDetached("explorer.exe", {"/select,\"c:\\space dir\\some.file\""});无法工作;但已弃用的QProcess::startDetached("explorer.exe /select,c:\\space dir\\some.file"});可以工作!请注意路径周围没有引号。 - KnowNothing
显示剩余6条评论

11

您可以使用 QFileDialog::getOpenFileName 来获取文件名。 相关文档可在此处获得here.. 如果有的话,上述函数将返回包括文件名和其扩展名在内的完整路径

然后,您可以使用

QDesktopServices::openUrl(path);

在默认应用程序中打开文件,其中path将是QFileDialog::getOpenFileName返回的QString

希望能对您有所帮助。


3
谢谢您的回答,但那不是我所需要的。我更新了问题以尝试澄清。我需要的是“在Finder中显示”或“在资源管理器中显示”的功能。 - nnc
QDesktopServices 的 openUrl() 方法实际上会在资源管理器中显示一个文件/目录。你是否在路径字符串前加了 "file:///"? - user925861
1
@liaK:OP只是想在资源管理器中显示文件show,而不是打开它。你的方法会尝试打开文件本身。 - diverger
2
只是为了澄清(五年后)...如果“path”是一个目录,openUrl(path)会在默认文件浏览器中打开该目录(它不会“显示”在常规意义上,这意味着打开父目录)。如果“path”是一个文件,则使用默认应用程序打开。您可以检测路径是否为文件,然后删除文件名(仅保留目录),但它不会在文件浏览器中选择文件。 - cbuchart
1
你不需要在路径字符串前加上 "file://";有QUrl::fromLocalFile();如果你使用相对路径,可能需要与QFile#absoluteFilePath()等一起使用。抄送@Stradivari - Jason C

6

根据之前一位回答的建议,这是我采用的代码。这个版本不依赖于Qt Creator中的其他方法,可以接受文件或目录,并且具有错误处理和适用于其他平台的备份模式:

void Util::showInFolder(const QString& path)
{
    QFileInfo info(path);
#if defined(Q_OS_WIN)
    QStringList args;
    if (!info.isDir())
        args << "/select,";
    args << QDir::toNativeSeparators(path);
    if (QProcess::startDetached("explorer", args))
        return;
#elif defined(Q_OS_MAC)
    QStringList args;
    args << "-e";
    args << "tell application \"Finder\"";
    args << "-e";
    args << "activate";
    args << "-e";
    args << "select POSIX file \"" + path + "\"";
    args << "-e";
    args << "end tell";
    args << "-e";
    args << "return";
    if (!QProcess::execute("/usr/bin/osascript", args))
        return;
#endif
    QDesktopServices::openUrl(QUrl::fromLocalFile(info.isDir()? path : info.path()));
}

我喜欢这种方法,因为它很直接。我将其移植到了python/PyQt5中,请看这里 - normanius
2
关于MacOS:请注意path必须是绝对路径。您可以通过添加return指令来抑制osascript的输出消息。 - normanius
还有一件事:在MacOS上,只需执行open dirPath即可更加简单明了,但如果path实际上是一个文件,则不会突出显示该文件。 - normanius
这应该是被接受的答案。非常感谢。 - Youda008
简单、短小,不需要特殊的东西就能运行。 - kluszon

5

在Windows资源管理器中打开文件(而不是浏览器)

void OpenFileInExplorer()
{
   QString path = "C:/exampleDir/example.txt";

   QStringList args;

   args << "/select," << QDir::toNativeSeparators(path);

   QProcess *process = new QProcess(this);
   process->start("explorer.exe", args); 

}

2
如果您使用带有Windows资源管理器的操作系统,那么很好,但我的大多数用户更喜欢避免使用它们。☺ - Michael Scheper
有点针对Windows的特定技巧,但非常好用,谢谢! - mrexodia
3
不跨平台。 - ZN13
您没有提供启动函数所需的第三个参数,而且这不是跨平台解决方案。 - Mushfiqur Rahman Abir

2

这个解决方案适用于Windows和Mac系统:

void showFileInFolder(const QString &path){
    #ifdef _WIN32    //Code for Windows
        QProcess::startDetached("explorer.exe", {"/select,", QDir::toNativeSeparators(path)});
    #elif defined(__APPLE__)    //Code for Mac
        QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\""});
        QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"});
    #endif
}

0
这里有一个类似于Dan Dennedy's answer的Linux选项,它利用DBus来使用默认的文件管理器。
#elif defined(Q_OS_LINUX)
    QStringList args;
    args << "--session";
    args << "--dest=org.freedesktop.FileManager1";
    args << "--type=method_call";
    args << "/org/freedesktop/FileManager1";
    args << "org.freedesktop.FileManager1.ShowItems";
    args << "array:string:file://" + path;
    args << "string:";
    if (QProcess::startDetached("dbus-send", args))
        return;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接