aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2018-12-08 17:04:31 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2018-12-08 17:04:31 +0000
commit8420639f57fda84b8b2431891b91fc056cedbaa5 (patch)
treea942adb24c36dfd3a5b32c3150656e4d70c8c8c1
parent95cc2f766a5bc896d3ab586f83f09a0aa9058f42 (diff)
downloadtkFileBrowser-8420639f57fda84b8b2431891b91fc056cedbaa5.tar.gz
tkFileBrowser-8420639f57fda84b8b2431891b91fc056cedbaa5.zip
fixed or noticed bugs, e.g. removing deleted files from multiple tabs
-rw-r--r--.vs/ProjectSettings.json3
-rw-r--r--.vs/slnx.sqlitebin0 -> 73728 bytes
-rw-r--r--.vs/tkFileBrowser/v15/.suobin0 -> 22016 bytes
-rw-r--r--fileTester.py25
-rw-r--r--testWinIcon.py17
-rw-r--r--tkFileBrowser.py186
-rw-r--r--winIcon.py52
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
new file mode 100644
index 0000000..1a1f4f7
--- /dev/null
+++ b/.vs/slnx.sqlite
Binary files differ
diff --git a/.vs/tkFileBrowser/v15/.suo b/.vs/tkFileBrowser/v15/.suo
new file mode 100644
index 0000000..6bfd20f
--- /dev/null
+++ b/.vs/tkFileBrowser/v15/.suo
Binary files differ
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