I have a directory with many subdirectories. Each subdirectory contains a small number of image files (.jpg). I would like to accumulate all these image files in a Tkinter listbox, sort them and be able to select blocks of files and send them to another listbox. Ultimately I would like to use the files in the selected file listbox and make a copy of those images in a separate directory.

I hope I made myself clear, any help?

This may be more difficult than you think. The files in the list need to have a full path name, see if the following list will do ...

# example walking a directory/folder
# create a list of all files with a given extension in
# a given the directory and all its subdirectories (full path name)

import os

def walk_dir(root_dir, extension):
    """
    walks the specified directory root and all its subdirectories
    and returns a list containing all files with extension ext
    """
    file_list = []
    #dir_list = []
    towalk = [root_dir]
    while towalk:
        root_dir = towalk.pop()
        for path in os.listdir(root_dir):
            path = os.path.join(root_dir, path).lower()
            if os.path.isfile(path) and path.endswith(extension):
                file_list.append(path)
            elif os.path.isdir(path):
                #dir_list.append(path)
                towalk.append(path)
    return file_list


# test the function
if __name__ == '__main__':
    # use the root directory of your choice
    root_dir = r"C:\temp"
    extension = '.jpg'
    file_list = walk_dir(root_dir, extension)
    
    file_list.sort()

    print "-"*70
    print "All files ending with %s in directory %s and its subdirectories:" % (extension, root_dir)
    for file in file_list:
        print file

Thanks vegaseat, I looked at the code and its output. Having the full path name for each file would be kind of ugly to select through. Isn't there a way to show just the file names without the directory in my listbox?

I put a few temporary print statements into your code to see how it all works. I think I am getting that part.

You will have to create a file_list of (filename, dirname) tuples. Load your listbox with just the filename (element zero of the tuple). When you transfer the selected filename to the second listbox, you actually transfer the full path tuple from the matching index in file_list to another list that parallels the second listbox. You need the full path info when you later on copy your selected files. It makes life a little harder, but is still relatively simple to do.

To show you what I mean, I threw this code together using previous code snippets ...

import os

from Tkinter import *

def walk_dir(root_dir, extension):
    """
    walks the specified directory root and all its subdirectories
    and returns a list of tuples containing all files with extension ext
    and their associated directory
    """
    file_list = []
    towalk = [root_dir]
    while towalk:
        root_dir = towalk.pop()
        for path in os.listdir(root_dir):
            full_path = os.path.join(root_dir, path).lower()
            if os.path.isfile(full_path) and full_path.endswith(extension):
                # create list of (filename, dir) tuples
                file_list.append((path.lower(), root_dir))
            elif os.path.isdir(full_path):
                towalk.append(full_path)
    return file_list

def get_list(event):
    """
    function to read the listbox selection(s)
    (mutliple lines can be selected with ctrl/shift mouse clicks)
    and put the result(s) in a label
    """
    try:
        global selpath
        # tuple of line index(es)
        sel = listbox1.curselection()
        # get the text, might be multi-line
        selpath = [file_list[int(x)] for x in sel]
    except:
        info_label.config(text="Please select a file on the list")

def add_file():
    """
    add selected file(s) from listbox1 to listbox2
    keep track of the (filename, dir) tuple too
    """
    try:
        for path_tuple in selpath:
            listbox2.insert(END, path_tuple[0])
            # this list exists parallel to the listbox2
            # and contains the associated directory info
            fullpath_list.append(path_tuple)
        #print ">>>", fullpath_list  #test
    except NameError:
        info_label.config(text="Please select a file on the list")

root = Tk()

# create the listbox (note that size is in characters)
# selectmode=EXTENDED allows ctrl/shift mouse slections
listbox1 = Listbox(root, width=40, height=6, selectmode=EXTENDED)
listbox1.grid(row=0, column=0)

# create listbox to transfer to
listbox2 = Listbox(root, width=40, height=6, bg='yellow')
listbox2.grid(row=0, column=2)
 
# create a vertical scrollbar to the right of the listbox1
yscroll = Scrollbar(command=listbox1.yview, orient=VERTICAL)
yscroll.grid(row=0, column=1, sticky=N+S+W)
listbox1.configure(yscrollcommand=yscroll.set)

# create a vertical scrollbar to the right of the listbox2
yscroll = Scrollbar(command=listbox2.yview, orient=VERTICAL)
yscroll.grid(row=0, column=4, sticky=N+S+W)
listbox2.configure(yscrollcommand=yscroll.set)

# button to transfer selected files from listbox1 to the listbox2
add_button = Button(root, text='Transfer selected file(s) from listbox1 to listbox2', command=add_file)
add_button.grid(row=2, column=0, pady=5, sticky=W)

# label to show progress information
info_label = Label(fg='red')
info_label.grid(row=3, column=0, pady=5, sticky=W)

# this keeps track of the selected file(s) full path for file copy
# whatever you do with listbox2, has to be done with this list too
fullpath_list = []

# use the root directory of your choice to select files from
root_dir = r"C:\temp"
# use the file exension you want
extension = '.jpg'
file_list = walk_dir(root_dir, extension)
# inplace sort the list
file_list.sort()

# load the listbox with data
for file in file_list:
    listbox1.insert(END, file[0])
    
# inform the user
file_num = len(file_list)
if  file_num > 0:
    info = "Loaded %d %s files from directory(+subs) %s" % (file_num, extension, root_dir)
else:
    info = "No %s files i directory %s" % (extension, root_dir)
info_label.config(text=info)
 
# left mouse click on a listbox1 line to display selection
listbox1.bind('<ButtonRelease-1>', get_list)
 
root.mainloop()

There are a few things missing, but the concept is there to select files with mouse clicks (you can use shift/ctrl mouse clicks too to select blocks of files), transfer them and keeping the dir info in a parallel list of tuples.

Wow, I am learning a lot. Your code was written like a tutorial, that helps a beginner like me! Thanks!

How do you copy the files in listbox2 to a different directory?

You use something like shutil.copy(source_file, destination_file). You can't use the items in listbox2 since that's only the filename, so you have to use the items in the companion fullpath_list. The source_file is made up from the tuple's dirname and filename components. The destination_file is made up from the destination directory and the same filename.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.