【问题标题】:Copy using the Windows copy dialog使用 Windows 复制对话框进行复制
【发布时间】:2013-05-27 21:14:04
【问题描述】:

我目前正在使用shutil.copy2() 复制大量图像文件和文件夹(介于 0.5 到 5 个演出之间)。 Shutil 工作正常,但是太慢了。我想知道是否有办法将此信息传递给 Windows 以制作副本并给我它的标准传输对话框。你知道,这家伙……

很多时候,我的脚本所花费的时间大约是标准 windows 复制所用时间的两倍,这让我担心我的 python 解释器在运行复制时挂起。我多次运行复制过程,希望缩短时间。

【问题讨论】:

  • 您是否确实使用 Python 和 Windows 资源管理器对同一文件的文件传输进行了定时?我很难相信 Python 实际上更慢。
  • 是的,我做了一个并排测试。它是跨网络的,所以可能是网络速度受到干扰,但是我如何使用 shutil 找到我的传输速度?
  • 您可以在 Python 中使用time.clock() 来获取传输时间,但您必须使用秒表来为 Explorer 计时。我的假设是 Python 和 Explorer 都调用相同的库来执行复制,但 Explorer 感觉更快,因为进度条可能是因为它给你的时间估计不正确。如果您同时运行两者并看到巨大的差异,那就很有趣了!
  • 我在网上发现了有关shutil 变慢的帖子......也许我会在某个时候做一个更科学的测试。无论哪种方式,我仍然希望将副本转储到 Windows,这样我就不必等待副本完成才能继续执行脚本。也许这是一个多线程问题?
  • 在另一天在本地和我们的网络上进行测试后,我发现shutil 的速度一样快。估计是网络流量问题。感谢您的帮助。

标签: python windows python-2.7


【解决方案1】:

如果您的目标是一个精美的复制对话框,SHFileOperation Windows API 函数可以提供。 pywin32 包有一个 python 绑定,ctypes 也是一个选项(例如谷歌“SHFileOperation ctypes”)。

这是我使用 pywin32 的(非常简单的测试)示例:

import os.path
from win32com.shell import shell, shellcon


def win32_shellcopy(src, dest):
    """
    Copy files and directories using Windows shell.

    :param src: Path or a list of paths to copy. Filename portion of a path
                (but not directory portion) can contain wildcards ``*`` and
                ``?``.
    :param dst: destination directory.
    :returns: ``True`` if the operation completed successfully,
              ``False`` if it was aborted by user (completed partially).
    :raises: ``WindowsError`` if anything went wrong. Typically, when source
             file was not found.

    .. seealso:
        `SHFileperation on MSDN <http://msdn.microsoft.com/en-us/library/windows/desktop/bb762164(v=vs.85).aspx>`
    """
    if isinstance(src, basestring):  # in Py3 replace basestring with str
        src = os.path.abspath(src)
    else:  # iterable
        src = '\0'.join(os.path.abspath(path) for path in src)

    result, aborted = shell.SHFileOperation((
        0,
        shellcon.FO_COPY,
        src,
        os.path.abspath(dest),
        shellcon.FOF_NOCONFIRMMKDIR,  # flags
        None,
        None))

    if not aborted and result != 0:
        # Note: raising a WindowsError with correct error code is quite
        # difficult due to SHFileOperation historical idiosyncrasies.
        # Therefore we simply pass a message.
        raise WindowsError('SHFileOperation failed: 0x%08x' % result)

    return not aborted

如果您将上面的标志设置为shellcon.FOF_SILENT | shellcon.FOF_NOCONFIRMATION | shellcon.FOF_NOERRORUI | shellcon.FOF_NOCONFIRMMKDIR.,您也可以在“静默模式”(无对话框、无确认、无错误弹出窗口)下执行相同的复制操作,详情请参阅SHFILEOPSTRUCT

【讨论】:

  • 我在这里询问了shell.SHFileOperation 的返回值以进行删除stackoverflow.com/questions/29053189/…。似乎找不到任何文档
  • 在这里我再次想知道src = os.path.abspath(src)是否应该是src = os.path.abspath(src) + '\0'(与下面的连接相同)
  • OSError: SHFileOperation failed: 0x000000b7
【解决方案2】:

更新:见

将它包装在一个库中会很高兴......在上面的答案的帮助下,我能够让它在 Windows 7 上运行,如下所示。

import pythoncom
from win32com.shell import shell,shellcon

def win_copy_files(src_files,dst_folder):           
        # @see IFileOperation
        pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)

        # Respond with Yes to All for any dialog
        # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
        pfo.SetOperationFlags(shellcon.FOF_NOCONFIRMATION)

        # Set the destionation folder
        dst = shell.SHCreateItemFromParsingName(dst_folder,None,shell.IID_IShellItem)

        for f in src_files:
                src = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
                pfo.CopyItem(src,dst) # Schedule an operation to be performed


        # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
        success = pfo.PerformOperations()

        # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
        aborted = pfo.GetAnyOperationsAborted()
        return success and not aborted


files_to_copy = [r'C:\Users\jrm\Documents\test1.txt',r'C:\Users\jrm\Documents\test2.txt']
dest_folder = r'C:\Users\jrm\Documents\dst'
win_copy_files(files_to_copy,dest_folder)

这里的参考资料也很有帮助: http://timgolden.me.uk/pywin32-docs/html/com/win32com/HTML/QuickStartClientCom.html

【讨论】:

  • 很抱歉这么晚才回复,但这看起来很有希望。运行此命令时出现错误,说明 shell 模块没有 CLSID_FileOperation 属性。
  • CLSID_FileOperation 不在 pywin32 的 pip 版本中。你有 pywin32 版本 218.4+ 吗?见github.com/frmdstryr/pywinutils
  • CLSID_FileOperation 在 pywin 220 中
  • 其实是在219中添加的:pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/raw-file/tip/…。然而,显然缺少某些功能 - 例如,我无法获得 IFileSystemBindData 接口的任何常量。
【解决方案3】:

IFileCopy。 IFileOperation 可能可以通过 ctypes 和 shell32.dll 获得,我不确定。

【讨论】:

  • ctypes.windll.shell32.SHFileOperation 存在,但 ctypes.windll.shell32.IFileOperation 不存在(Windows 10,Python 3.8)
【解决方案4】:

*碰撞* Windows 10!

在您的帮助下和Virgil Dupras' send2trash:
我刚刚使用ctypes 做了一个vanilla Python 版本:

import os
import ctypes
from ctypes import wintypes


class _SHFILEOPSTRUCTW(ctypes.Structure):
    _fields_ = [("hwnd", wintypes.HWND),
                ("wFunc", wintypes.UINT),
                ("pFrom", wintypes.LPCWSTR),
                ("pTo", wintypes.LPCWSTR),
                ("fFlags", ctypes.c_uint),
                ("fAnyOperationsAborted", wintypes.BOOL),
                ("hNameMappings", ctypes.c_uint),
                ("lpszProgressTitle", wintypes.LPCWSTR)]


def win_shell_copy(src, dst):
    """
    :param str src: Source path to copy from. Must exist!
    :param str dst: Destination path to copy to. Will be created on demand.
    :return: Success of the operation. False means is was aborted!
    :rtype: bool
    """
    if not os.path.exist(src):
        print('No such source "%s"' % src)
        return False

    src_buffer = ctypes.create_unicode_buffer(src, len(src) + 2)
    dst_buffer = ctypes.create_unicode_buffer(dst, len(dst) + 2)

    fileop = _SHFILEOPSTRUCTW()
    fileop.hwnd = 0
    fileop.wFunc = 2  # FO_COPY
    fileop.pFrom = wintypes.LPCWSTR(ctypes.addressof(src_buffer))
    fileop.pTo = wintypes.LPCWSTR(ctypes.addressof(dst_buffer))
    fileop.fFlags = 512  # FOF_NOCONFIRMMKDIR
    fileop.fAnyOperationsAborted = 0
    fileop.hNameMappings = 0
    fileop.lpszProgressTitle = None

    result = ctypes.windll.shell32.SHFileOperationW(ctypes.byref(fileop))
    return not result

✔ 已在 Python 3.7 和 2.7 上进行测试,同时具有长 src 和 dst 路径。

【讨论】:

  • 最有用的答案,但是我读到 IFileOperation 已经取代了自 Vista 以来的 SHFileOperationW。你有没有找到从 ctypes 中使用它的方法?
  • 谢谢!但不是。我还不知道IFileOperation。我可以想象ctypes.windll 需要为此更新。我的意思是“shell32”听起来不像 > 2000 年:|
  • 也许在 Python 3.9 中 :)
猜你喜欢
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 2012-04-26
相关资源
最近更新 更多