小能豆

如何使用 ctrl+c 和 ctrl+v 将文件夹从一个文件夹复制、剪切到另一个文件夹

py

我的标题可能看起来有点模棱两可,所以这里有一个解释。

专业 IDE 喜欢PycharmVisual Studio Code允许复制文件夹、导航到特定目录并将其粘贴在那里。我也想实现这一点。

但就我而言,shutil.copytree 需要 2 个参数 - 源文件夹和目标文件夹。

那么,有没有什么方法可以复制文件夹,浏览资源管理器,单击粘贴或按下,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()

```


阅读 9

收藏
2024-11-19

共1个答案

小能豆

注意:这个答案并没有回答 OP 的问题,因为它可以将内容从外部文件浏览器复制到 tkinter 应用程序中选择的文件夹中,但不能像 OP 所希望的那样进行相反的操作。

首先,为了更容易地检索项目的绝对路径,我使用绝对路径作为树中的项目标识符。

然后,为了实现粘贴部分,我添加了一个.paste()名为 with Ctrl+ 的方法V。在此方法中,我通过获取当前选定的项目来获取目标文件夹。如果此项目是文件,则我使用父文件夹作为目标。我从剪贴板获取要复制的文件/文件夹的路径。如果它是一个文件,我使用shutil.copy2(src, dest)。由于即使文件已经存在于中也会复制该文件dest,因此您可能需要在之前添加一些代码来检查并显示消息框。如果源是一个文件夹,我使用shutil.copytree(src, os.path.join(dest, src_dirname))wheresrc_dirname是复制文件夹的名称。

正如评论中所建议的,我使用了 tkinter 的方法.clipboard_clear().clipboard_append().clipboard_get()不是使用clipboard模块。

在 中.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 应用程序复制到外部文件浏览器的部分实现:此方向复制的问题在于它是特定于平台的,因为不同平台对剪贴板的处理方式不同。以下解决方案在LinuxXFCE桌面环境中以及使用Thunar文件浏览器时对我有效。

我使用klembord库来访问系统的剪贴板,其中包含比纯文本更丰富的内容。如果文件/文件夹已使用以下命令复制到剪贴板,则可以在 Thunar 中粘贴该文件/文件夹

klembord.set({'x-special/gnome-copied-files': f'copy\nfile://{abspath}'.encode()})

其中abspath是文件/文件夹的 HTML 转义绝对路径。

为了实现这一点App,导入klembordurllib.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()})
2024-11-19