Note: This answer does not answer the OP's question as it makes it possible to copy from an external filebrowser into the folder chosen in tkinter application, but not the opposite, as wanted by the OP.
Firstly, to make retrieving the absolute paths of the items easier, I use the absolute path as item identifier in the tree.
Then, to implement the pasting part, I added a .paste()
method, called with Ctrl+V. In this method, I obtain the destination folder by getting the currently selected item. If this item is a file, then I use the parent folder as the destination. I get the path of the file/folder to copy from the clipboard. If it is a file, I use shutil.copy2(src, dest)
. As it will copy the file even if it already exists in dest
, you will probably want to add some code before to check that and show a messagebox. If the source is a folder, I use shutil.copytree(src, os.path.join(dest, src_dirname))
where src_dirname
is the name of the copied folder.
As suggested in the comments, I used tkinter's methods .clipboard_clear()
, .clipboard_append()
and .clipboard_get()
instead of using the clipboard
module.
In .copy_to_clipboard()
, I suggest that you use self.tree.focus()
instead of self.tree.identify_row(y)
, so as to get the selected item, not the one below the mouse cursor (I have just added a comment next to the relevant line in the code but not implemented this suggestion).
Here is the code:
import os
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.messagebox import showerror
import shutil
import traceback
class App(tk.Frame):
def __init__(self, master, path):
tk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self)
ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(self, orient='horizontal', command=self.tree.xview)
self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)
self.tree.heading('#0', text=path, anchor='w')
abspath = os.path.abspath(path)
self.tree.insert('', 'end', abspath, text=abspath, open=True)
self.process_directory(abspath)
self.tree.bind("<Control-c>", self.copy_to_clipboard)
self.tree.bind("<Control-v>", self.paste)
self.tree.grid(row=0, column=0)
ysb.grid(row=0, column=1, sticky='ns')
xsb.grid(row=1, column=0, sticky='ew')
self.grid()
def copy_to_clipboard(self, event, *args):
item = self.tree.identify_row(event.y) # you may want to use self.tree.focus() instead so that
# the selected item is copied, not the one below the mouse cursor
self.clipboard_clear()
self.clipboard_append(item)
def paste(self, event):
src = self.clipboard_get()
if not os.path.exists(src):
return
dest = self.tree.focus()
if not dest:
dest = self.tree.get_children("")[0] # get root folder path
elif not os.path.isdir(dest): # selected item is a file, use parent folder
dest = self.tree.parent(dest)
if os.path.isdir(src):
try:
dirname = os.path.split(src)[1]
newpath = shutil.copytree(src, os.path.join(dest, dirname))
self.tree.insert(dest, "end", newpath, text=dirname)
self.process_directory(newpath)
self.tree.item(dest, open=True)
except Exception:
showerror("Error", traceback.format_exc())
else:
try:
# you might want to check if the file already exists in dest and ask what to do
# otherwise shutil.copy2() will replace it
newpath = shutil.copy2(src, dest)
self.tree.insert(dest, "end", newpath, text=os.path.split(src)[1])
except tk.TclError: # the item already exists
pass
except Exception:
showerror("Error", traceback.format_exc())
def process_directory(self, path):
try:
for p in os.listdir(path):
abspath = os.path.join(path, p)
isdir = os.path.isdir(abspath)
# use abspath as item IID
self.tree.insert(path, 'end', abspath, text=p, open=False)
if isdir:
self.process_directory(abspath)
except PermissionError:
pass
root = tk.Tk()
path_to_my_project = '/tmp/truc'
app = App(root, path=path_to_my_project)
app.mainloop()
Partial implementation of copying from the tkinter application into an external filebrowser: The issue with copying in this direction is that it is platform specific as the clipboard is handled differently by different platforms. The following solution works for me in Linux, in the XFCE desktop environment and using Thunar filebrowser.
I used the klembord library to access the system's clipboard with richer content than just plain text. It is possible to paste a file/folder in Thunar if it has been copied to the clipboard with
klembord.set({'x-special/gnome-copied-files': f'copy\nfile://{abspath}'.encode()})
where abspath
is the HTML-escaped absolute path of the file/folder.
To implement this into App
, import klembord
and urllib.parse
and replace
self.clipboard_clear()
self.clipboard_append(item)
in .copy_to_clipboard()
by
klembord.set({'x-special/gnome-copied-files':
f'copy\nfile://{urllib.parse.quote(item)}'.encode()})