diff --git a/README.txt b/README.txt index 744f2eb..14ada7f 100644 --- a/README.txt +++ b/README.txt @@ -1 +1,12 @@ -A simple app that manages your downloads folder. \ No newline at end of file +A simple app that manages your downloads folder. + +Features: + - File list view, separated by type + - File name, date modified, size + - Buttons + - Start Task, Refresh, Backup + - Status bar + - Rules manager + - Per file type: Move to dir, copy to dir, delete, or ignore + - Custom define file types + - Save and load rules config \ No newline at end of file diff --git a/app.py b/app.py index a380210..d663149 100644 --- a/app.py +++ b/app.py @@ -4,12 +4,15 @@ __name__ = "__main__" __author__ = "Gull" import tkinter as tk -from tkinter import ttk # Modern widgets import file_manager as fm +import utils as ut +from tkinter import ttk +from tkinter.messagebox import showinfo debug_mode = False def set_debug_mode(value): + global debug_mode if type(value) == None: debug_mode = not debug_mode else: @@ -19,6 +22,7 @@ class App(tk.Tk): def __init__(self, width=600, height=400): super().__init__() self.title("Download Utils") + self.option_add('*tearOff', tk.FALSE) self.fm = fm.Manager() self.gui() @@ -30,17 +34,27 @@ class App(tk.Tk): self.geometry(f"{width}x{height}+{center_x}+{center_y}") - def update_file_list(self): - for n in range(self.listbox_file.size(), 0, -1): - self.listbox_file.delete(n - 1) - self.fm.update_file_list() - for v in self.fm.filelist: - self.listbox_file.insert(tk.END, v) + def update_fileview(self): + self.fmgr = fm.Manager() + self.fmgr.set_directory(self.fmgr.get_directory()) + self.fmgr.update_file_data() + + while len(self.fileview.get_children()) > 0: + self.fileview.delete(self.fileview.get_children()[-1]) + + for k, v in self.fmgr.filedata.items(): + values = (k, str(ut.format_date(v["time"])), + str(ut.format_bytes(v["size"]))) + self.fileview.insert("", tk.END, k, values=values) def gui(self): self.gui_menu() - self.gui_side() - self.gui_main() + + self.book = ttk.Notebook(self) + self.book.add(self.gui_files(), text="Files") + self.book.add(self.gui_rules(), text="Rules") + + self.book.pack(expand=True, fill=tk.BOTH) def gui_menu(self): a_menu = tk.Menu(self) # First arg is always the parent @@ -64,25 +78,52 @@ class App(tk.Tk): menu=a_menu_settings, underline=0 ) + + def gui_files(self): + frame = ttk.Frame(self.book) + + box = ttk.Frame(frame) + + button = ttk.Button(box, text="Start Task") + button.grid(column=0, row=0, pady=5) + + button = ttk.Button(box, text="Refresh", command=self.update_fileview) + button.grid(column=1, row=0, pady=5) + + button = ttk.Button(box, text="Backup") + button.grid(column=2, row=0, pady=5) + + box.pack(side=tk.TOP, fill=tk.X) + + self.fileview_columns = ("Name", "Last Modified", "Size") + self.fileview = ttk.Treeview(frame, columns=self.fileview_columns, + show="headings") - def gui_side(self): - a_side = ttk.Frame(self, width=80, style="TLabel") - a_side.pack(side=tk.LEFT, fill=tk.Y) + for v in self.fileview_columns: + self.fileview.heading(v, text=v) + self.fileview.column(v, width=100, anchor=tk.W) - tk_style = ttk.Style(self) - tk_style.configure("TLabel", background="#bbbbbb") + sbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, + command=self.fileview.yview) + self.fileview.configure(yscrollcommand=sbar.set) - def gui_main(self): - a_content = ttk.Frame(self) + self.update_fileview() - a_button = ttk.Button(a_content, text="Refresh", command=self.update_file_list) - a_button.pack(side=tk.TOP) + self.fileview.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + sbar.pack(side=tk.RIGHT, fill=tk.Y) - self.listbox_file = tk.Listbox(a_content, height=20) - self.update_file_list() - self.listbox_file.pack(padx=10, pady=10, side=tk.LEFT, expand=True, fill=tk.BOTH) + return frame + + def gui_rules(self): + frame = ttk.Frame(self.book) - a_content.pack(side=tk.LEFT, expand=True, fill=tk.BOTH) + # Display a simple panel on the left having file meta types + + # Mode: + # Target dir: + # File extensions: + + return frame if __name__ == "__main__": app = App() diff --git a/file_manager.py b/file_manager.py index 47427ed..a556243 100644 --- a/file_manager.py +++ b/file_manager.py @@ -1,4 +1,4 @@ -"""File manager for the app.""" +"""File manager and organizer for the app.""" __author__ = "Gull" @@ -7,18 +7,38 @@ import json class Manager: def __init__(self): - self.dir = self.get_directory() - self.filelist = [] + self.dir = os.path.expanduser("~") # Default directory + self.filedata = dict() + self.filetypes = { + "images": ["jpg", "jpeg", "png", "gif", "webp"], + "audio": ["wav", "mp3", "ogg"], + "video": ["wmv", "mpg", "mpeg", "avi"], + "text": ["txt", "doc", "docx"], + "data": ["csv", "json"], + "programs": ["exe", "msi"], + "archives": ["zip", "gz", "rar"], + "other": [] + } - def get_directory(self): + def get_directory(self, target="Downloads"): dir = os.path.expanduser("~") - dir = os.path.join(dir, "Downloads") + dir = os.path.join(dir, target) if os.path.exists(dir): # Needs error handling return dir - def update_file_list(self): - self.filelist.clear() + def set_directory(self, dir): + if os.path.exists(dir): + self.dir = dir + + def modify_type_list(self, key, values): + if key in self.filetypes: + self.filetypes[key] = list(values) + + def update_file_data(self): + self.filedata.clear() with os.scandir(self.dir) as scan: for entry in scan: if entry.is_file(): - self.filelist.append(entry.name) \ No newline at end of file + st = entry.stat() + data = {"time": st.st_mtime, "size": st.st_size} + self.filedata.update({entry.name: data}) \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..cada610 --- /dev/null +++ b/utils.py @@ -0,0 +1,31 @@ +"""Utility functions.""" + +__author__ = "Gull" + +import time + +KILOBYTES = 1024 + +def format_bytes(bytes): + """Return the given bytes as a human friendly KB, MB, GB, or TB string.""" + units = { + "B": float(1), + "KB": float(KILOBYTES), + "MB": float(KILOBYTES ** 2), # 1,048,576 + "GB": float(KILOBYTES ** 3), # 1,073,741,824 + "TB": float(KILOBYTES ** 4) # 1,099,511,627,776 + } + + if bytes < units["KB"]: + return f"{bytes} B" + elif units["KB"] <= bytes < units["MB"]: + return f"{(bytes / units["KB"]):.2f} KB" + elif units["MB"] <= bytes < units["GB"]: + return f"{(bytes / units["MB"]):.2f} MB" + elif units["GB"] <= bytes < units["TB"]: + return f"{(bytes / units["GB"]):.2f} GB" + elif units["TB"] <= bytes: + return f"{(bytes / units["TB"]):.2f} TB" + +def format_date(timestamp): + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp)) \ No newline at end of file