Browse for a folder

Introduction

The requirement: Open a dialog box to allow the user to select a folder

Although a little involved (as everything is in the shell API) this is actually quite straightforward. The main stumbling block with understanding anything to do with the shell API is the ubiquitous PIDL (which, together with its component the SHITEM must raise a titter wherever English-speaking programmers gather).

There's no room here for a long exposition on the ITEMIDLIST structures, but fortunately there's no need either. All you need to know here is that, rather than through filesystem paths around, the shell API uses PIDLs, which it translates to and from real or virtual filesystems. You can use functions such as SHGetFolderLocation and SHGetPathFromIDList to convert to and fro.

A little explanation as to the params to SHBrowseForFolder:

import win32gui
from win32com.shell import shell, shellcon

desktop_pidl = shell.SHGetFolderLocation (0, shellcon.CSIDL_DESKTOP, 0, 0)
pidl, display_name, image_list = shell.SHBrowseForFolder (
  win32gui.GetDesktopWindow (),
  desktop_pidl,
  "Choose a folder",
  0,
  None,
  None
)
print shell.SHGetPathFromIDList (pidl)

You can do loads of things by juggling with the flags parameter (the zero in the example above): include shellcon.BIF_BROWSEINCLUDEFILES to select files as well as folders; include shellcon.BIF_BROWSEFORCOMPUTER to limit the selections to computers on the network. Obviously you can also vary the starting point of the display, say to My Documents, by changing the desktop_pidl assignment above to use a different CSIDL_ constant or by calling shell.SHILCreateFromPath to convert an arbitrary path to a PIDL.

Here's an example which browses for a file, starting from the user's My Documents, and then uses ShellExecute to open that using the default application.

import os
import win32gui
from win32com.shell import shell, shellcon

mydocs_pidl = shell.SHGetFolderLocation (0, shellcon.CSIDL_PERSONAL, 0, 0)
pidl, display_name, image_list = shell.SHBrowseForFolder (
  win32gui.GetDesktopWindow (),
  mydocs_pidl,
  "Select a file or folder",
  shellcon.BIF_BROWSEINCLUDEFILES,
  None,
  None
)

if (pidl, display_name, image_list) == (None, None, None):
  print "Nothing selected"
else:
  path = shell.SHGetPathFromIDList (pidl)
  print "Opening", path
  os.startfile (path)