我的标题可能看起来有点模棱两可,所以这里有一个解释。
专业 IDE 喜欢Pycharm或Visual Studio Code允许复制文件夹、导航到特定目录并将其粘贴在那里。我也想实现这一点。
Pycharm
Visual Studio Code
但就我而言,shutil.copytree 需要 2 个参数 - 源文件夹和目标文件夹。
那么,有没有什么方法可以复制文件夹,浏览资源管理器,单击粘贴或按下,ctrl+v然后文件夹就会被复制或粘贴到那里,而不像shutil.copytree用户已经需要提供路径的地方?
ctrl+v
shutil.copytree
目前,我有一个将文件夹名称复制到剪贴板的代码。
import os import tkinter as tk import tkinter.ttk as ttk import clipboard 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) root_node = self.tree.insert('', 'end', text=abspath, open=True) self.process_directory(root_node, abspath) self.tree.bind("<Control-c>",self.copy_to_clipboard) 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) clipboard.copy(self.tree.item(item,"text")) def process_directory(self, parent, path): try: for p in os.listdir(path): abspath = os.path.join(path, p) isdir = os.path.isdir(abspath) oid = self.tree.insert(parent, 'end', text=p, open=False) if isdir: self.process_directory(oid, abspath) except PermissionError: pass root = tk.Tk() path_to_my_project = 'C:\\Users\\91996\\Documents' app = App(root, path=path_to_my_project) app.mainloop()
```
注意:这个答案并没有回答 OP 的问题,因为它可以将内容从外部文件浏览器复制到 tkinter 应用程序中选择的文件夹中,但不能像 OP 所希望的那样进行相反的操作。
首先,为了更容易地检索项目的绝对路径,我使用绝对路径作为树中的项目标识符。
然后,为了实现粘贴部分,我添加了一个.paste()名为 with Ctrl+ 的方法V。在此方法中,我通过获取当前选定的项目来获取目标文件夹。如果此项目是文件,则我使用父文件夹作为目标。我从剪贴板获取要复制的文件/文件夹的路径。如果它是一个文件,我使用shutil.copy2(src, dest)。由于即使文件已经存在于中也会复制该文件dest,因此您可能需要在之前添加一些代码来检查并显示消息框。如果源是一个文件夹,我使用shutil.copytree(src, os.path.join(dest, src_dirname))wheresrc_dirname是复制文件夹的名称。
.paste()
shutil.copy2(src, dest)
dest
shutil.copytree(src, os.path.join(dest, src_dirname))
src_dirname
正如评论中所建议的,我使用了 tkinter 的方法.clipboard_clear(),.clipboard_append()而.clipboard_get()不是使用clipboard模块。
.clipboard_clear()
.clipboard_append()
.clipboard_get()
clipboard
在 中.copy_to_clipboard(),我建议您使用self.tree.focus()而不是self.tree.identify_row(y),以便获取选定的项目,而不是鼠标光标下方的项目(我只是在代码中在相关行旁边添加了注释,但没有实现这个建议)。
.copy_to_clipboard()
self.tree.focus()
self.tree.identify_row(y)
以下是代码:
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()
从 tkinter 应用程序复制到外部文件浏览器的部分实现:此方向复制的问题在于它是特定于平台的,因为不同平台对剪贴板的处理方式不同。以下解决方案在Linux、XFCE桌面环境中以及使用Thunar文件浏览器时对我有效。
我使用klembord库来访问系统的剪贴板,其中包含比纯文本更丰富的内容。如果文件/文件夹已使用以下命令复制到剪贴板,则可以在 Thunar 中粘贴该文件/文件夹
klembord.set({'x-special/gnome-copied-files': f'copy\nfile://{abspath}'.encode()})
其中abspath是文件/文件夹的 HTML 转义绝对路径。
abspath
为了实现这一点App,导入klembord和urllib.parse替换
App
klembord
urllib.parse
self.clipboard_clear() self.clipboard_append(item)
在.copy_to_clipboard()
klembord.set({'x-special/gnome-copied-files': f'copy\nfile://{urllib.parse.quote(item)}'.encode()})