Win32gui... file dialogs?

Alex Martelli alex at magenta.com
Sun Aug 27 09:47:46 EDT 2000


"Kevin Cazabon" <kcazabon at home.com> wrote in message
news:l1Zp5.210819$8u4.2129533 at news1.rdc1.bc.home.com...
    [snip]
> -how would you set initial directory and file?
> -how would you set filters (multiple types, or a single selected type with
> multiple extensions)

The current version (see later) lets you do all of these (and a bit more).
Still a small fraction of the common-file-dialog's actual flexibility, but
more of it can be added if need be.

> It'd be nice if we could come up with a dummy-proof Python script that
> allowed you to interface with the dialog through standard Python args...
> I'm sure there's more than one person out there that would appreciate it
as
> much as I...

No doubt, which is why I've proceeded with the development.

> Can I convince you to help me one step further?  q:]

Sure, here comes the newest version of ofn.py...:


import win32gui, struct, array, string
"""ofn.py: Win32 functions to get 1 or more filenames for Open or Save,
   via user interaction with the file-select Common Dialog.
"""
# A. Martelli, 2000-08-27; placed in the public domain.

OFN_ALLOWMULTISELECT=0x00000200
OFN_EXPLORER=0x00080000

def arrayToStrings(resultArray):
    """return list-of-strings corresponding to a char array,
       where each string is terminated by \000, and the whole
       list by two adjacent \000 bytes
    """
    astr=resultArray.tostring()
    manyStrings=[]
    # perhaps a loop of string.split would be faster...
    while len(astr) and astr[0]!='\000':
        i=astr.index('\000')
        manyStrings.append(astr[:i])
        astr=astr[i+1:]
    return manyStrings

def szFrom(anarray):
    """return the string-pointer (sz) corresponding to a char
       array, 0 (null pointer) if no array
    """
    if anarray: return anarray.buffer_info()[0]
    else: return 0

def arrayFrom(astring,additional=0):
    """return a char array built from a string, plus 0
       or more \000 bytes as filler
    """
    if not astring: astring=''
    return array.array('c',astring+additional*'\000')

def arrayMulti(stringlist):
    """return a char array built from many strings, each
       separated by a \000 byte, and two \000's at the end
    """
    return arrayFrom(string.join(stringlist,'\000'),2)

def buildOfn(resultarray,filters=None,initdir=None,title=None,
             multisel=1,oldlook=0):
    """build an OPENFILENAME struct as a string, with several
       options and a given result-array for the string[s] that
       will result from the GetOpenFileName call
    """
    flags=OFN_EXPLORER
    if multisel: flags=flags|OFN_ALLOWMULTISELECT
    if oldlook: flags=flags&~OFN_EXPLORER
    szfile,maxfile=resultarray.buffer_info()
    szfilter=szFrom(filters)
    szinitdir=szFrom(initdir)
    sztitle=szFrom(title)
    return struct.pack(
        "3i2P2iPiPi2PI2hPi2P",
        76, 0, 0,           # size, owner-hwnd, hinstance
        szfilter, 0, 0, 0,  # filter, custom-filter, max-cust-filter,
filter-index
        szfile, maxfile,    # file, max-file
        0, 0,               # file-title, max-file-title
        szinitdir, sztitle, # initial-dir, dialog-title
        flags, 0, 0,        # flags, file-offset, file-extension
        0,                  # def-ext
        0, 0, 0)            # cust-data, func-hook, template-name

def openNames(forsave=0,filters=None,initdir=None,title=None,
              initfile=None,multisel=1,oldlook=0):
    """return a list of filenames for open or save, given
       interactively by the user through a common-dialog; if
       more than 1 string is returned, the first is the directory,
       followed by the filenames.
    """
    resultBuffer=arrayFrom(initfile,8192)
    title=arrayFrom(title)
    initdir=arrayFrom(initdir)
    filters=arrayMulti(filters)
    ofn=buildOfn(resultBuffer,filters,initdir,title,multisel,oldlook)
    if forSave: isok=win32gui.GetSaveFileName(ofn)
    else: isok=win32gui.GetOpenFileName(ofn)
    if not isok: return []
    return arrayToStrings(resultBuffer)

def _test():
    return openNames(
        filters=('Texts and scripts','*.txt;*.py','Py stuff','*.py*')
    )

if __name__=='__main__':
    print _test()


This ofn.openNames() offers only a subset of the flexibility of the
underlying GetOpenFileName/GetSaveFileName calls, but I've
tried to make it a reasonable subset!  The filters argument is
a list of strings, alternatively the string that's displayed for that
filter in the dialog's combobox, and the corresponding mask
(one or more wildcard-containing expressions separated by
semicolons). initdir, title (dialog's title) and initfile, if present,
are each just a Python string. forsave, multisel and oldlook are
just boolean values (multisel defaults to true, the others to
false). The return value is a list of:
- 0 strings, if the user has clicked Cancel on the dialog,
- 1 string, the full pathname, if 1 file was selected and OK'd
- n+1 strings, if n files were selected and OK'd: first the
    directory in which the files reside, then the n files themselves
    (for each, just the filename.ext; path is not repeated).

Anything important still missing...?

Testing has been spotty -- let me know of any problems and I'll
try to reproduce and solve them.


Alex







More information about the Python-list mailing list