62

Is it possible to open a folder in Windows Explorer/OS X Finder and then select/highlight one file in that folder, and do it in a cross platform way? Right now, I do something like

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

where path is a full path to folder I want to open. Obviously, this will just open the folder, and I'll have to track down the file I need manually. This is a bit of a problem when there are thousands of files in that folder.

If I make it a path to specific file in that folder, then that file is open with default application for that mime type, and that is not what I need. Instead, I need the functionality equivalent to "Reveal in Finder" or "Show in Explorer".

nnc
  • 1,003
  • 2
  • 8
  • 9
  • To clarify: you mean like when you click a "Reveal in Finder" or "Show in Explorer" menu item? – Austin Hyde Aug 16 '10 at 03:28
  • @Austin Yes, what I meant is exactly something like "Reveal in Finder" or "Show in Explorer" – nnc Aug 16 '10 at 03:48
  • [I've factored out this functionality from Qt Creator to implement simple tooling to automate creation of screenshots for SO example code :)](https://github.com/KubaO/stackoverflown/blob/master/tooling/showinshell.cpp). – Kuba hasn't forgotten Monica Aug 03 '18 at 01:11

5 Answers5

50

Qt Creator (source) has this functionality, it's trivial to copy it:

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);
    }
}

Another, related blog post (with simpler code, I haven't tried it so I can't comment), is this.

Edit:

There is a bug in the original code when pathIn contains spaces on Windows. QProcess::startDetached will automatically quote a parameter if it contains spaces. However, Windows Explorer will not recognize a parameter wrapped in quotes, and will open the default location instead. Try it yourself in the Windows command line:

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"

Thus,

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

is changed to

QString command = explorer + " " + param;
QProcess::startDetached(command);
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
Ivo
  • 3,481
  • 1
  • 25
  • 29
  • 1
    I tested the code in the blog post you linked. It does work (at least on Mac OS X). – Sylvain Defresne Feb 14 '11 at 10:23
  • If you need to open path that contains spaces you can use something like this: QProcess::startDetached(QString("explorer /select, \"%1\"").arg(QDir::toNativeSeparators(path))); – dismine Nov 29 '15 at 12:01
  • Before I try to port all that to PyQt, can you confirm it works in Linux, file selection and all? The comment 'we cannot select a file here, because no file browser really supports it' doesn't sound promising. – Michael Scheper Jun 22 '16 at 09:53
  • 1
    @Michael Scheper: I ported DanDennedy's answer to PyQt5 (see [here](https://pastebin.com/xrcE3a2L)). I haven't tested it for Linux, but it is quite straightforward to look at. – normanius Feb 04 '18 at 01:43
  • What is `Environment` class? – Tomáš Zato Feb 06 '18 at 16:47
  • `Environment` https://github.com/qt-creator/qt-creator/blob/master/src/libs/utils/environment.cpp – Neil Z. Shao Jun 28 '18 at 07:36
  • 1
    Your edit is incorrect. Explorer accepts a quote-wrapped path just fine (on Windows 10, at least). The key is that the explorer args must be comma-separated, so the original source code is correct when it uses `/select,` (with a comma) not `/select`. Qt will correctly wrap the path in quotes as necessary. Source: http://www.geoffchappell.com/studies/windows/shell/explorer/cmdline.htm – Phlucious Apr 25 '19 at 21:13
  • 2
    Posting GPL code and suggest copy & paste? It's a trap! – user2366975 Oct 07 '19 at 11:20
  • 1
    it it very exact about spaces part, even in Qt 5.15.0. `QProcess::startDetached("explorer.exe", {"/select,c:\\space dir\\some.file"});` couldn't work; `QProcess::startDetached("explorer.exe", {"/select,\"c:\\space dir\\some.file\""});` couldn't work; but deprecated `QProcess::startDetached("explorer.exe /select,c:\\space dir\\some.file"});` works! And notice that there is no quotes arround the path. – KnowNothing May 15 '21 at 03:32
  • @KnowNothing: thanks for the info, but the suggested way of `startDetached(command)` also works with no manual quoting required, just tested on Windows 10. – Violet Giraffe Oct 22 '21 at 12:13
  • @Ivo Well it's not trivial to copy, because `HostOsInfo`, `Environment`, `UnixUtils`, `ICore` are internal utils of the library and not available in the public API. We would have to rip whole parts out of Qt library and put them into our project. – Youda008 Nov 07 '21 at 09:42
11

Probably you can use QFileDialog::getOpenFileName to get the file name. The documentation is available here.. The above function will return the complete path including the file name and its extension if any..

Then you can give

QDesktopServices::openUrl(path);

to open the the file in its default appication, where the path will be the QString returned by QFileDialog::getOpenFileName.

Hope it helps..

liaK
  • 11,422
  • 11
  • 48
  • 73
  • 3
    thanks for the answer, but that is not what I need. I updated the question to try to clarify that. What I need is "Reveal in Finder" or "Show in Explorer" functionality. – nnc Aug 16 '10 at 04:51
  • The openUrl()-Method of QDesktopServices actually shows a file/directory in the explorer. Did you prepend "file:///" on your path string? –  Oct 15 '14 at 08:50
  • 1
    @liaK: The OP just want the file **show** in the explorer, not **open** it. Your method will try to **open** the file itself. – diverger May 26 '15 at 02:06
  • 2
    Just to clarify (5 years later)... if "path" is a directory, the openUrl(path) opens that directory in the default file explorer (it doesn't "reveal" in the common sense, which means to open the parent directory). If "path" is a file, it opens with the default application. You can detect if path is a file and then remove the filename (keeping just the directory), but it won't select the file in the file explorer. – cbuchart Jul 29 '15 at 09:18
  • 1
    You don't have to prepend "file://" to the path string; there is [`QUrl::fromLocalFile()`](http://doc.qt.io/qt-4.8/qurl.html#fromLocalFile); you may have to use it with `QFile#absoluteFilePath()`, etc. if you're working with relative paths. cc @Stradivari – Jason C Dec 09 '15 at 14:33
6

Here is the code that I went with based on the inputs from a previous answer. This version does not depend on other methods in Qt Creator, accepts a file or directory, and has a fall back mode for error handling and other platforms:

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()));
}
Dan Dennedy
  • 446
  • 4
  • 6
  • I like this approach because it's straightforward. I ported this to python/PyQt5, have a look [here](https://pastebin.com/xrcE3a2L). – normanius Feb 04 '18 at 01:36
  • 2
    As for MacOS: Note that `path` has to be absolute. One can suppress output messages from `osascript` by simply adding a `return` instruction. – normanius Feb 04 '18 at 01:39
  • And one more thing: Just executing `open dirPath` would be even more straightforward on MacOS, but it does not highlight the file if `path` was actually a file. – normanius Feb 04 '18 at 01:48
  • This should be the accepted answer. Thanks a lot. – Youda008 Nov 07 '21 at 12:47
  • Simple, short, and works without special things. – kluszon Oct 14 '22 at 12:10
5

Open file in windows explorer (not browser)

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); 

}
Mara Black
  • 1,666
  • 18
  • 23
2

This solution works on both Windows and 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
}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99