Hello everyone!
Here is my approach to an application to organize the files you have on any folder on your computer sorting them according to their extension.
This was also the first time I experimented creating a (super simple) user interface on python instead of having the user doing everything on the terminal. I used the module Tkinter
to create this window for the user to interact with.
- Creating the User Interface
- Pressing the Start Button
- The organize() function
- Installing the Application
- Closing notes
Creating the User Interface
The first step was creating the main window. Using the Tkinter module, we can create an object of the class tkinter.Tk.
In my case, I called it root as most of the examples I found on using this module.
# Creating the main window for the user interface.
root = Tk()
After that, I started personalizing my root
object or main window. Changing the default icon for a free personalized icon, I found at freeicons.io (thanks to Ginkaewicons who created the icon.), setting the window geometry and adding a title to it.
# Replace the default icon
try:
root.iconbitmap('organizer.ico')
except:
pass
# Setting up the geometry of the window.
root.geometry('320x195')
# By setting both parameters to false the user can not resize the window.
root.resizable(False, False)
# We make sure the program gets the main focus on the user's Desktop.
root.focus()
# Title of the main window
root.title('Organizer.py')
Next, I decided on the widgets I wanted the main window to have.
In my case, I decided to add:
- A Label that will give information to the user about what the program does, what it is expecting, or when it has finished.
- A Start Button which will start the primary function of the program.
- A Progress Bar that gives the user a visual reference that tells him that the program is doing something and when it has finished.
# Creating our start button, which will start the program.
startbutton = tk.Button(
root, # Setting the 'master' window for this widget
text = 'Start', # Setting the text to display on this widget
command = find_dir, # This will be the function that executes when you press the button.
cursor='hand2', # This will change the cursor when the user hovers it over the button.
)
# A description label to give information to the user about the program status.
description_label = tk.Label(
root, # Setting the 'master' window for this widget
# Setting the text to display on this widget
text = 'You can use this program to organize all your different \ntype of files in folders\n\nJust choose the folder that you want to organize\nby clicking the Start button below',
justify= 'left' # With this, every new line will start at the left of the label.
)
# A progress bar for the user to follow the program's progress.
progressbar = ttk.Progressbar(
root, # Setting the 'master' window for this widget
orient=HORIZONTAL, # Setting the progress bar horizontally
length = 240, # Setting the lenght of the progress bar in pixels
mode = 'determinate' # We know when our program finishes so we use the determinate mode
)
You can find information about all of the attributes of these and more widgets here.
Finally, I placed the objects in the main window:
#Placing the objects we created for our user interface.
description_label.place(x=4, y= 5)
startbutton.place(x=140, y= 140)
progressbar.pack(side='bottom', pady=5)
Since the user can not resize the window, I placed the label and button at specific positions using the x and y axes. For the progress bar, I put it at the bottom of the window, adding a 5px padding on top and bottom to avoid having it entirely on the window's border.
After doing this we get our main window:
Pressing the Start Button
Now that the main window is done the start button will be the one responsible of doing what the main goal of the program is. Once it is pressed it will call the find_dir()
function which will prompt the user about which folder he wants to organize using the filedialog
method from the Tkinter module.
# The find dir function will ask the user for the folder he wants to organize.
# In case he presses 'Cancel', the `dirname` variable will be empty
# In this case, we ask the user to please select a folder to be able to organize it
def find_dir():
global description_label
frm = ttk.Frame(root, padding=10)
ttk.Label(frm, text="Select the folder that you want to organize")
dirname = filedialog.askdirectory(parent=root, initialdir='~', title='Select the folder that you want to organize')
# If the user closes the file dialog or presses cancel the dirname will be empty in this case, we change the text on the label of the main window and do nothing else.
if dirname == '':
description_label['text'] = 'Please select a folder to continue\n\nJust choose the folder that you want to organize\nby clicking the Start button below'
else:
# Once we have the directory that the user wants to organize we pass it to the function organize()
organize(dirname)
If the user closes the window asking for the folder or presses 'Cancel' the program will return to the main window and inform him that a folder is needed for the program to work allowing him to press the start button to start the process again.
After that, if the user selected a folder, two things could happen:
- There are no files on the selected folder, in which case the program will inform the user about it and give him the option to press the start button again if he wants to.
Message displayed when a folder with no files was selected
- There are files on the selected folder in which case the program will call the
organize()
function which will then organize all the files into folders.
The organize() function
The organize()
function is in charge of putting every file in the correct folder. To achieve this, I created different categories of files, and using match case
, I assigned every file to a folder. In case there is an extension that doesn't fall into any of the categories I created, the program will put it into a folder called Others
I won't put the portion of the code that does this here because it's a little bit too long, but you can check the code here to see the categories I created and what extension falls into each category.
Thanks to file-extensions.org, the place from where I got lists of the most common file extensions and got the idea of how to name each category.
Installing the Application
You can find the code for the application at this repository at GitHub.
You can also download the executable on this link
Closing notes
Feel free to contact me through my email (public on my profile) if you find any issue or problem with the application or if you have any advice about how something might be better implemented. You can also try adding more stuff to the application; I would love to see that.
Thanks.
Top comments (6)
Cool, and thanks for sharing this.
I also found myself with a particular need for a file tool and took the path of writing one in Python and turned to Tkinter when I wanted a GUI for it. It's also GPL3 and can be found at Foldatry. It was also my first ever Python program so it started small.
What your does is something I've occasionally needed so I'll be sure to give it a try next time that comes up.
p.s. a first try at running it failed as it appears to require Python version 3.10 - for the use of
match case
Hi geraldew, thanks for your comment!
Yes, you are right for
match case
to work, you need to have python 3.10 at least.However, you can also download the zip file with the executable only here if you just want to use the program. In that case, it should work without issues.
Do let me know if you find any issue when using it so I can check that out.
I also had a look at your repository, and it seems like an excellent app with much more functionalities than mine haha.
Thanks again!
Well, I didn't feel like changing my Python version (that's a whole other topic) so I figured I'd just rewrite the
match case
construct to the older style.Except, when I noticed all the logical Or symbols I was prompted to re-imagine how I would handle that sort of thing. I decided that I preferred the run-time handling to be just looking into a dictionary rather than doing a cascade of boolean comparisons.
So after some looking at other code I'd written here's what I came up with.
The prep work is to define a look-up resource - implemented as instructions for making two layers of dictionary.
I used an enumeration as the link between the two - in effect this is a translation of your various
case
groups.Then, to the top of:
I added a line:
so that constructs an instance of the nested dictionaries.
Then, instead of your
match
structure, I do:By the way, inside
def make_ext_lookups():
I added a check to tell me if I'd miskeyed when I adapted the extension lists. This was done withif ext in dct_extensions:
and theprint
that it does.I wasn't actually expecting that to show anything, but as it happened, it did - printing the following:
So you might want to check your source code for similar double presences in your
case
lines.Anyway, now that I have a variant that runs of my older Python (which will be just whatever is installed on Xubuntu 20.04) - it seems to work nicely.
I don't know that I like "Others" as a folder name for unrecognised things - I'd prefer something either alphabetically before or after all the rest.
Hi geraldew,
It took me a moment to understand how your solution was working since I'm still learning a lot of new stuff and had not used enum before but after testing for a while with the debugger I understood how you solved the sorting using enum and dictionaries. I wanted to ask you, does this make the code run more efficiently? or was it just your workaround to not use match case? I'm not sure yet how to test when a code is more or less efficient so I would appreciate if you could tell me if there is a difference in performance with one approach or the other.
Regarding the duplicates I did have a look and found out that the source from where I got the extensions list does have some extensions listed in multiple categories which is why your code found the use of the same extension on different categories. I guess I'll have to manually decide to which categories I want those extensions to be sorted out.
Thanks again!
Well, I certainly made the change to not use
match case
.My main reaction though was because I don't like seeing so much data-like material being hard coded. Where I can, I like to move that kind of thing into a data structure. This often has the advantage of making the code simpler at the point of decision.
But another advantage is that it prepares the ground for maybe loading that decision data from a config file, say from a JSON file. That way, fine tuning of what the program does can be done without rewriting it.
As for which is more "efficient" that might depend on quite what meaning you want for that.
As you had a
match case
construct, answering that will partly depend on quite how that gets implemented under the hood, i.e. by CPython. Double-guessing (or even checking the CPython source code) seems to be a popular game in some quarters. My personal view is that if that's a worry then its probably time to code in something other than Python. It is an interpreted scripting language after all.Anyway, I wasn't really comparing to the match case, rather to the complex of
if
andelif
blocks I would need and also the number ofOr
operators - just to replace what you had.When I see a lot of
Or
usage I tend to remember that the speed of the operation becomes quite variable depending on the data. It was that thought of variance that prompted me to think that for each distinct extension, we (the programmers) already know which categorisation should be used. So how do we express that best in Python? Well in short, using a known value to get another known related value is what dictionaries are good at.I strongly suspect that the dictionary lookup is faster than a lot of cascading conditional logic operators, but it does beg the question of how dictionaries are handled by CPython.
For that matter, I could have constructed a dictionary that directly mapped from extension to sub-folder - rather than the double-dictionary method that I first wrote. Thus are the many, many options of tackling these things.
As for when to construct those dictionaries, that comes down to knowing the scope and lifespan of the program. As a quick thing to do, I put that construction step - that is, calling the function to do it - inside the
def organize(directory)
. But the way your program currently works, it could have been done outside that, thereby only be done once to cover all runs oforganize
. I was just too lazy to work which way to do that: make it global, pass it in as a control parameter etc.BTW another thing that the as-data approach enables is having alternate dictionaries to pass to the organise function. For example, there could be some stock but varied combination for the user to select among.
Hi geraldew,
Sorry for the delay I was offline for a few days, yes I can see what you mean by loading the decision from a file by doing it using data structures instead of hard coding it and re-writing the code each time you want to add or remove something.
As for the efficiency topic I was asking more because right now I'm reading about time and space complexity and was curious if may be this was something that played a role on why you decided to do it like that. If I'm not mistaken python uses a garbage collector so space complexity is out of the hands of the programmer (I think) but I'm still not sure how to know when a program will be more or less time complex.
Thanks for your comments you have shared some really interesting stuff ^^