<p><strong>注意</strong>:此答案不能回答OP的问题,因为它可以从外部文件浏览器复制到tkinter应用程序中选择的文件夹中,但不能按照OP的要求复制到相反的文件夹中</p>
<p>首先,为了便于检索项目的绝对路径,我在树中使用绝对路径作为项目标识符</p>
<p>然后,为了实现粘贴部分,我添加了一个<code>.paste()</code>方法,用<kbd>Ctrl</kbd>+<kbd>V</kbd>调用。在这种方法中,我通过获取当前选定的项目来获取目标文件夹。如果此项目是一个文件,则我使用父文件夹作为目标。我从剪贴板获取要复制的文件/文件夹的路径。如果它是一个文件,我使用<code>shutil.copy2(src, dest)</code>。因为即使文件已经存在于<code>dest</code>中,它也会复制该文件,所以您可能需要在选中该文件并显示messagebox之前添加一些代码。如果源是文件夹,我使用<code>shutil.copytree(src, os.path.join(dest, src_dirname))</code>,其中<code>src_dirname</code>是复制文件夹的名称</p>
<p>正如评论中所建议的,我使用了tkinter的方法<code>.clipboard_clear()</code>、<code>.clipboard_append()</code>和<code>.clipboard_get()</code>,而不是使用<code>clipboard</code>模块</p>
<p>在<code>.copy_to_clipboard()</code>中,我建议您使用<code>self.tree.focus()</code>而不是<code>self.tree.identify_row(y)</code>,以便获取所选项目,而不是鼠标光标下方的项目(我刚刚在代码中的相关行旁边添加了一条注释,但没有实现此建议)</p>
<p>代码如下:</p>
<pre><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()
</code></pre>
<p><strong>从tkinter应用程序复制到外部文件浏览器的部分实现:</strong>在这个方向上复制的问题是,它是特定于平台的,因为不同平台对剪贴板的处理方式不同。以下解决方案适用于我在<strong>Linux</strong>中、在<strong>XFCE</strong>桌面环境中以及使用<strong>Thunar</strong>文件浏览器</p>
<p>我使用<a href="https://pypi.org/project/klembord/" rel="nofollow noreferrer">klembord</a>库访问系统的剪贴板,其内容比纯文本更丰富。如果已将文件/文件夹复制到剪贴板,则可以在Thunar中粘贴该文件/文件夹</p>
<pre><code>klembord.set({'x-special/gnome-copied-files': f'copy\nfile://{abspath}'.encode()})
</code></pre>
<p>其中<code>abspath</code>是文件/文件夹的HTML转义绝对路径</p>
<p>要将其实现到<code>App</code>,请导入<code>klembord</code>和<code>urllib.parse</code>并替换</p>
<pre><code>self.clipboard_clear()
self.clipboard_append(item)
</code></pre>
<p>在<code>.copy_to_clipboard()</code>中</p>
<pre><code>klembord.set({'x-special/gnome-copied-files':
f'copy\nfile://{urllib.parse.quote(item)}'.encode()})
</code></pre>