diff options
| author | jwansek <eddie.atten.ea29@gmail.com> | 2018-12-08 17:04:31 +0000 |
|---|---|---|
| committer | jwansek <eddie.atten.ea29@gmail.com> | 2018-12-08 17:04:31 +0000 |
| commit | 8420639f57fda84b8b2431891b91fc056cedbaa5 (patch) | |
| tree | a942adb24c36dfd3a5b32c3150656e4d70c8c8c1 | |
| parent | 95cc2f766a5bc896d3ab586f83f09a0aa9058f42 (diff) | |
| download | tkFileBrowser-8420639f57fda84b8b2431891b91fc056cedbaa5.tar.gz tkFileBrowser-8420639f57fda84b8b2431891b91fc056cedbaa5.zip | |
fixed or noticed bugs, e.g. removing deleted files from multiple tabs
| -rw-r--r-- | .vs/ProjectSettings.json | 3 | ||||
| -rw-r--r-- | .vs/slnx.sqlite | bin | 0 -> 73728 bytes | |||
| -rw-r--r-- | .vs/tkFileBrowser/v15/.suo | bin | 0 -> 22016 bytes | |||
| -rw-r--r-- | fileTester.py | 25 | ||||
| -rw-r--r-- | testWinIcon.py | 17 | ||||
| -rw-r--r-- | tkFileBrowser.py | 186 | ||||
| -rw-r--r-- | winIcon.py | 52 |
7 files changed, 223 insertions, 60 deletions
diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..f8b4888 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +}
\ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite Binary files differnew file mode 100644 index 0000000..1a1f4f7 --- /dev/null +++ b/.vs/slnx.sqlite diff --git a/.vs/tkFileBrowser/v15/.suo b/.vs/tkFileBrowser/v15/.suo Binary files differnew file mode 100644 index 0000000..6bfd20f --- /dev/null +++ b/.vs/tkFileBrowser/v15/.suo diff --git a/fileTester.py b/fileTester.py new file mode 100644 index 0000000..65a2f2d --- /dev/null +++ b/fileTester.py @@ -0,0 +1,25 @@ +import tkinter as tk +from tkinter import ttk +import os + +def on_close(): + if os.path.exists(os.path.join("E:", "temp")): + os.rmdir(os.path.join("E:", "temp")) + if os.path.exists(os.path.join(os.path.expanduser("~"), "temp")): + os.rmdir(os.path.join(os.path.expanduser("~"), "temp")) + + root.destroy() + +root = tk.Tk() +root.title("File tester program") +root.resizable(False, False) +root.protocol("WM_DELETE_WINDOW", on_close) + +tk.Label(root, text = "File tester program", font = ("Verdana", 16, "bold")).grid(row = 0, column = 0, columnspan = 2, padx = 3, pady = 3) + +ttk.Button(root, text = "Add in user", command = lambda: os.mkdir(os.path.join(os.path.expanduser("~"), "temp"))).grid(row = 1, column = 0, padx = 3, pady = 3, ipady = 3, ipadx = 3) +ttk.Button(root, text = "Delete in user", command = lambda: os.rmdir(os.path.join(os.path.expanduser("~"), "temp"))).grid(row = 1, column = 1, padx = 3, pady = 3, ipady = 3, ipadx = 3) +ttk.Button(root, text = "Add in E:\\", command = lambda: os.mkdir(os.path.join("E:", "temp"))).grid(row = 2, column = 0, padx = 3, pady = 3, ipady = 3, ipadx = 3) +ttk.Button(root, text = "Delete in E:\\", command = lambda: os.rmdir(os.path.join("E:", "temp"))).grid(row = 2, column = 1, padx = 3, pady = 3, ipady = 3, ipadx = 3) + +root.mainloop()
\ No newline at end of file diff --git a/testWinIcon.py b/testWinIcon.py new file mode 100644 index 0000000..99ccfe1 --- /dev/null +++ b/testWinIcon.py @@ -0,0 +1,17 @@ +l = [['C:\\Users\\Edward', 'C:\\Users\\Edward\\Documents'], ['C:\\Users\\Edward', 'C:\\Users\\Edward\\Documents\\random_pyapps'], ['C:\\Users\\Edward', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser'], ['C:\\Users\\Edward', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser']] + +out = [] +search = 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser' + + +def get_drive_and_path(search): + out = [] + for i in l: + if i[1] == search: + out.append(i) + return out + +print(get_drive_and_path(search)) + +[['C:\\', 'C:\\Users'], ['C:\\', 'C:\\Users\\Edward'], ['C:\\', 'C:\\Users\\Edward\\Documents'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser\\empty_folder'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser\\temp']] +[['C:\\', 'C:\\Users'], ['C:\\', 'C:\\Users\\Edward'], ['C:\\', 'C:\\Users\\Edward\\Documents'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser\\empty_folder'], ['C:\\', 'C:\\Users\\Edward\\Documents\\random_pyapps\\tkFileBrowser\\tkFileBrowser\\temp']]
\ No newline at end of file diff --git a/tkFileBrowser.py b/tkFileBrowser.py index 2374131..0b64d52 100644 --- a/tkFileBrowser.py +++ b/tkFileBrowser.py @@ -2,22 +2,15 @@ from glob import glob from operator import itemgetter import tkinter as tk from tkinter import ttk -from win32com.shell import shell, shellcon from tkinter import messagebox from PIL import Image, ImageTk import win32api -import win32con -import win32ui -import win32gui +import winIcon import os #stuff to do: -# fix the icon error, even though I literally have changed nothing to do with them -# and they've stopped working for some reason - # make the files actually refresh instead of just detecting it -# keep current nodes open when a new drive is plugged in class TkFileBrowser(tk.Frame): @@ -48,6 +41,7 @@ class TkFileBrowser(tk.Frame): self._book.pack(fill = tk.BOTH, expand = True) self.after(self._refresh, self.refresh) + #TODO: fix a bug here def see(self, path): """Open the tree and book to this folder @@ -55,7 +49,7 @@ class TkFileBrowser(tk.Frame): path {str} -- path to open to """ if not os.path.exists(path): - raise FileNotFoundError("The system couldn't find the path: '%s'" % path) + print("The system couldn't find the path: '%s'" % path) return #open the correct book tab @@ -83,11 +77,16 @@ class TkFileBrowser(tk.Frame): self._book._tabs[drive]._tree.see("\\".join(split)) def refresh(self): - self._book._refresh() + def get_drive_and_path(search): + out = [] + for i in self._open: + if i[1] == search: + out.append(i) + return out - #TODO: update root nodes too + self._book._refresh() - #print(self._open) + print(self._open) #work out which open nodes need to be updated nodes_to_refresh = [] @@ -96,23 +95,117 @@ class TkFileBrowser(tk.Frame): if not os.path.exists(dir): continue #work out all the dirs that the node is showing, ignoring the dummy by checking for paths - childirs = [p.split("\\")[-1] for p in self._book._tabs[i[0]]._tree.get_children(dir) if os.path.exists(p)] + childirs = [p.split("\\")[-1] for p in self._book._tabs[i[0]]._tree.get_children(dir) if "\\" in p] #work out all the dirs that are in the file system. Use our own method so that settings #are still here, show hidden files, etc. files, folders = self._book._tabs[i[0]]._get_dirs_in_path(dir) actualdirs = files + folders - print(dir, actualdirs == childirs) + if actualdirs != childirs: + nodes_to_refresh.append(dir) + + + #add drives to the list of stuff to check for updates + for drive in self._book._get_drives(): + childirs = [p.split("\\")[-1] for p in self._book._tabs[drive]._tree.get_children("")] + files, folders = self._book._tabs[drive]._get_dirs_in_path(drive) + actualdirs = files + folders + if actualdirs != childirs: + nodes_to_refresh.append(drive) + + #print(nodes_to_refresh) + + drive_and_node = [] + for node in nodes_to_refresh: + for drive, node1 in get_drive_and_path(node): + + orignode = node1 + if node1 == drive: + node1 = "" + + #print("node: ", node1, "\ndrive: ", drive) + tree = self._book._tabs[drive]._tree + children = tree.get_children(node1) + + #get the full path of removals and additons + glob_pattern = os.path.join(orignode, "*") + deletions = list(set(list(children)) - set(sorted(glob(glob_pattern), key=os.path.getctime))) + additions = list(set(sorted(glob(glob_pattern), key=os.path.getctime)) - set(list(children))) + + #print("deletions: ", deletions, "\nadditions: ", additions) + + #delete from tree + for deletion in deletions: + tree.delete(deletion) + #if the node is open, and it's just been deleteted, remove it from the list + if [drive.replace("\\", "\\"), deletion.replace("\\", "\\")] in self._open: + self._open.remove([drive.replace("\\", "\\"), deletion.replace("\\", "\\")]) + + # #add new entries in the correct place, highlighting them and getting appropitate icons + # for addition in additions: + + # if os.path.isdir(addition): + # #make an icon if it doesn't already exist + # if addition not in self._book._foldericons: + # self._book._foldericons[addition] = ImageTk.PhotoImage( + # self._get_icon(addition)) + + # #get the name + # folder = addition.split("\\")[-1] + + # tree.insert( + # parent = node, + # index = os.listdir(orignode).index(folder), #insert in an appropriate place + # iid = addition, + # tags = (addition, ), + # text = folder, + # image = self._book._foldericons[addition], + # values = [folder, "", ""]) + # #setup a dummy so the '+' appears before it's loaded. Child stuff will be loaded when the user + # #clicks on the plus, and the dummy will be removed. (or not if there are no files) + # tree.insert(parent = addition, index = tk.END, tag = "dummy", text = "No avaliable files") + + # elif os.path.isfile(addition): + # _, type_ = os.path.splitext(addition) + + # if type_ == ".lnk" or type_ == ".exe": + # if addition not in self._book._fileicons: + # self._book._fileicons[addition] = ImageTk.PhotoImage( + # self._get_icon(addition) + # ) + # icon = self._book._fileicons[addition] + # else: + # if type_ not in self._book._fileicons: + # self._book._fileicons[type_] = ImageTk.PhotoImage( + # self._get_icon(type_) + # ) + # icon = self._book._fileicons[type_] + + # file = addition.split("\\")[-1] + # tree.insert( + # parent = node, + # index = tk.END, + # iid = addition, + # tag = addition, + # text = file, + # image = icon, + # values = [file, "", self._book._tabs[drive]._get_size(addition)]) + + # #highlight + # tree.tag_configure(addition, background = "orange") + + + + self.after(self._refresh, self.refresh) - def _get_icon(self, PATH, size): + def _get_icon(self, PATH): """Gets the icon association for any folder or file in the system Arguments: PATH {str} -- path to file or folder - size {str} -- equal to "small" or "large". Indicates to return the 16x16 image or 32x32 image Raises: TypeError -- Thrown if invalid arguments are given @@ -123,37 +216,8 @@ class TkFileBrowser(tk.Frame): #https://stackoverflow.com/questions/21070423/python-sAaving-accessing-file-extension-icons-and-using-them-in-a-tkinter-progra/52957794#52957794 #https://aecomputervision.blogspot.com/2018/10/getting-icon-association-for-any-file.html - SHGFI_ICON = 0x000000100 - SHGFI_ICONLOCATION = 0x000001000 - if size == "small": - SHIL_SIZE = 0x00001 - elif size == "large": - SHIL_SIZE = 0x00002 - else: - raise TypeError("Invalid argument for 'size'. Must be equal to 'small' or 'large'") - - ret, info = shell.SHGetFileInfo(PATH, 0, SHGFI_ICONLOCATION | SHGFI_ICON | SHIL_SIZE) - hIcon, iIcon, dwAttr, name, typeName = info - ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON) - hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0)) - hbmp = win32ui.CreateBitmap() - hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_x) - hdc = hdc.CreateCompatibleDC() - hdc.SelectObject(hbmp) - hdc.DrawIcon((0, 0), hIcon) - win32gui.DestroyIcon(hIcon) - - bmpinfo = hbmp.GetInfo() - bmpstr = hbmp.GetBitmapBits(True) - img = Image.frombuffer( - "RGBA", - (bmpinfo["bmWidth"], bmpinfo["bmHeight"]), - bmpstr, "raw", "BGRA", 0, 1 - ) - - if size == "small": - img = img.resize((16, 16), Image.ANTIALIAS) - return img + return winIcon.get_icon(PATH, winIcon.SMALL) + class DriveBook(ttk.Notebook): @@ -188,7 +252,7 @@ class DriveBook(ttk.Notebook): removals = list(set(display).difference(new)) additions = list(set(new).difference(display)) - #print("removed: ", removals, "added: ", additions) + print("removed: ", removals, "added: ", additions) for removal in removals: #delete open nodes that have just been removed @@ -207,18 +271,17 @@ class DriveBook(ttk.Notebook): #reset drive icons self._drive_icons = [i for i in self._drive_icons if i[0] != removal] - if additions != []: - for tab in self.tabs(): - self.forget(tab) - - self._draw_tabs() + for addition in additions: + self._drive_icons.append([addition, ImageTk.PhotoImage(self._parent._get_icon(addition))]) + self._tabs[addition] = FileTree(self, addition) + self.add(self._tabs[addition], text = addition, image = self._drive_icons[-1][1], compound = tk.LEFT) def _get_drives(self): return [os.path.expanduser("~")] + win32api.GetLogicalDriveStrings().split('\x00')[:-1] def _get_icons(self): drives = self._get_drives() - return [[drive, ImageTk.PhotoImage(self._parent._get_icon(drive, "small"))] for drive in drives] + return [[drive, ImageTk.PhotoImage(self._parent._get_icon(drive))] for drive in drives] def _get_tab_name(self): """Returns the name of the tab which is currently open @@ -278,7 +341,7 @@ class FileTree(tk.Frame): self._tree.delete(child) self._populate_path(id) - print("\n clicked: ", self._parent._parent._open) + #print("\n clicked: ", self._parent._parent._open) def _on_close(self, event): """Method called when the user closes a node. This must delete the node, @@ -348,7 +411,7 @@ class FileTree(tk.Frame): parent = node, index = tk.END, iid = fullpath, - tag = fullpath, + tags = (fullpath, ), text = folder, image = self._parent._foldericons[fullpath], values = [folder, "", ""]) @@ -402,12 +465,15 @@ class FileTree(tk.Frame): #if the file is a shortcut, set the key as the whole path if type_ == ".lnk" or type_ == ".exe": - self._parent._fileicons[os.path.join(path, p)] = ImageTk.PhotoImage( - self._parent._parent._get_icon(os.path.join(path, p), "small")) + if os.path.join(path, p) not in self._parent._fileicons: + self._parent._fileicons[os.path.join(path, p)] = ImageTk.PhotoImage( + self._parent._parent._get_icon(os.path.join(path, p))) else: if type_ not in self._parent._fileicons: self._parent._fileicons[type_] = ImageTk.PhotoImage( - self._parent._parent._get_icon(os.path.join(path, p), "small")) + self._parent._parent._get_icon(os.path.join(path, p))) + + #print(self._parent._fileicons, "\n") files = [] folders = [] @@ -427,7 +493,7 @@ class FileTree(tk.Frame): folders.append(p) if os.path.join(path, p) not in self._parent._foldericons: self._parent._foldericons[os.path.join(path, p)] = ImageTk.PhotoImage( - self._parent._parent._get_icon(os.path.join(path, p), "small")) + self._parent._parent._get_icon(os.path.join(path, p))) #print("\n\nfolders: ", folders, "\nfiles: ", files) return folders, files diff --git a/winIcon.py b/winIcon.py new file mode 100644 index 0000000..2a40a05 --- /dev/null +++ b/winIcon.py @@ -0,0 +1,52 @@ +from win32com.shell import shell, shellcon +from PIL import Image, ImageTk +import win32api +import win32con +import win32ui +import win32gui +import os + +LARGE = "large" +SMALL = "small" + +def get_icon(PATH, size): + #print("\nPath: ", PATH, "Type: ", type(PATH), "Count: ", count, "\n") + + SHGFI_ICON = 0x000000100 + SHGFI_ICONLOCATION = 0x000001000 + if size == "small": + SHIL_SIZE = 0x00001 + elif size == "large": + SHIL_SIZE = 0x00002 + else: + raise TypeError("Invalid argument for 'size'. Must be equal to 'small' or 'large'") + + try: + ret, info = shell.SHGetFileInfo(PATH, 0, SHGFI_ICONLOCATION | SHGFI_ICON | SHIL_SIZE) + hIcon, iIcon, dwAttr, name, typeName = info + ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON) + hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0)) + hbmp = win32ui.CreateBitmap() + hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_x) + hdc = hdc.CreateCompatibleDC() + hdc.SelectObject(hbmp) + hdc.DrawIcon((0, 0), hIcon) + win32gui.DestroyIcon(hIcon) + + bmpinfo = hbmp.GetInfo() + bmpstr = hbmp.GetBitmapBits(True) + img = Image.frombuffer( + "RGBA", + (bmpinfo["bmWidth"], bmpinfo["bmHeight"]), + bmpstr, "raw", "BGRA", 0, 1 + ) + except win32ui.error: + raise WinIconError("Couldn't get icon for '%s'" % PATH) + return + + if size == "small": + img = img.resize((16, 16), Image.ANTIALIAS) + return img + +class WinIconError(Exception): + pass
\ No newline at end of file |
