Python:在资源管理器中获取所选文件的列表(WIndows 7)

时间:2014-01-20 18:43:48

标签: python contextmenu windows-explorer

在工作中我可以选择多个.xlsx文件,右键单击一个文件将为我提供组合文件以制作.pdf的选项。现在我想在我的一个脚本中使用相同的功能。也就是说,选择多个文件并将这些文件的路径作为参数发送到Python脚本。

我花了一个小时寻找解决方案,但我没有找到任何好的答案。似乎有一些C#答案,但我不知道如何将代码转换为Python。是否有可能实现它?

编辑 - 示例脚本:

import sys, os
for file in sys.argv:
    print(file)
os.system("PAUSE")

4 个答案:

答案 0 :(得分:2)

编辑:至少在使用上下文菜单

时无法使用

我找到了部分解决方案here。但是,如果Internet Explorer处于打开状态,它将无法工作(如何检测到这一点?)。此外,如果打开了多个Windows资源管理器实例,则会计算所有窗口中的所选文件。我为此添加了一张支票。

import win32com.client as win32
import os
import win32ui


def explorer_fileselection():
    working_dir = os.getcwd()

    clsid = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}' #Valid for IE as well!
    shellwindows = win32.Dispatch(clsid)

    files = []
    try:
        for window in range(shellwindows.Count):
            window_URL = shellwindows[window].LocationURL
            window_dir = window_URL.split("///")[1].replace("/", "\\")
            if window_dir == working_dir:
                selected_files = shellwindows[window].Document.SelectedItems()
                for file in range(selected_files.Count):
                    files.append(selected_files.Item(file).Path)
    except:   #Ugh, need a better way to handle this one
        win32ui.MessageBox("Close IE!", "Error")
    del shellwindows

    return files


print(*explorer_fileselection(), sep="\n")

--- prints the selected files:

C:\Users\oe\Python\ssa\util\test3.docx
C:\Users\oe\Python\ssa\util\__init__.py
C:\Users\oe\Python\ssa\util\explorer_filer.py
C:\Users\oe\Python\ssa\util\test1.xlsx
C:\Users\oe\Python\ssa\util\test2.xls

我想我会在函数中添加*valid_ext参数,因此我可以像explorer_fileselection("xlsx", "xls")这样调用来获取Excel文件。

答案 1 :(得分:1)

这实际上是一个Windows问题,并不是非常具体的Python。 您希望Windows shell在shell上下文菜单中显示脚本的菜单项。

要完成此操作,您可以向注册表添加一些密钥。有关如何添加菜单项的说明,请参阅Add menu item to windows context menu only for specific filetype

之后,当您选择多个文件并将它们发送到脚本时,您将看到这些文件为命令行参数。如果选择10个文件,脚本将运行10次。

答案 2 :(得分:1)

我知道这很晚才在此处发布答案,但是几个月前我曾尝试过Olav的解决方案,但并未完全起作用:工作目录是脚本的工作目录,因此我不得不删除如果条件使其正常工作,但它选择了所有Windows资源管理器窗口中的所有文件(我也希望这样做,因此对我来说部分起作用)。但是现在我回到继续我的项目(助理)的过程中,发现我确实需要这项工作,因此我考虑了这个想法(这并不难想到,但花了几个月的时间我才有了... )。我不知道这个答案是否对其他任何人都有用,但是对我来说,它并不完全,所以我认为我可以改进它,并在此处发布解决方案。这段代码混合了以下答案(我在同一脚本中也一直使用该答案,但从未想过让它们一起工作):https://stackoverflow.com/a/43892579/8228163(我更正为至少在Windows 7下可以工作)和Olav的答案和结果对我有用-该脚本仅在当前Windows资源管理器窗口中检测文件。我认为所有这些工作都可以从Vista(也许我不知道它早于7)到10,但是我不确定。另一个答案是使用XP。当我在Windows 10上启动此脚本时,我认为它可以工作,但是我已经没有10了,所以我不确定(我再次使用7,所以7可以正常工作)。

import win32gui, time
from win32con import PAGE_READWRITE, MEM_COMMIT, MEM_RESERVE, MEM_RELEASE, PROCESS_ALL_ACCESS, WM_GETTEXTLENGTH, WM_GETTEXT
from commctrl import LVS_OWNERDATA, LVM_GETITEMCOUNT, LVM_GETNEXTITEM, LVNI_SELECTED
import os
import struct
import ctypes
import win32api
import datetime
import win32com.client as win32
import win32ui
import psutil
import subprocess
import time
import urllib.parse

clsid = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}' #Valid for IE as well!

def getEditText(hwnd):
    # api returns 16 bit characters so buffer needs 1 more char for null and twice the num of chars
    buf_size = (win32gui.SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0) +1 ) * 2
    target_buff = ctypes.create_string_buffer(buf_size)
    win32gui.SendMessage(hwnd, WM_GETTEXT, buf_size, ctypes.addressof(target_buff))
    return target_buff.raw.decode('utf16')[:-1]# remove the null char on the end

def _normaliseText(controlText):
    '''Remove '&' characters, and lower case.
    Useful for matching control text.'''
    return controlText.lower().replace('&', '')

def _windowEnumerationHandler(hwnd, resultList):
    '''Pass to win32gui.EnumWindows() to generate list of window handle,
    window text, window class tuples.'''
    resultList.append((hwnd, win32gui.GetWindowText(hwnd), win32gui.GetClassName(hwnd)))

def searchChildWindows(currentHwnd,
               wantedText=None,
               wantedClass=None,
               selectionFunction=None):
    results = []
    childWindows = []
    try:
        win32gui.EnumChildWindows(currentHwnd,
                      _windowEnumerationHandler,
                      childWindows)
    except win32gui.error:
        # This seems to mean that the control *cannot* have child windows,
        # i.e. not a container.
        return
    for childHwnd, windowText, windowClass in childWindows:
        descendentMatchingHwnds = searchChildWindows(childHwnd)
        if descendentMatchingHwnds:
            results += descendentMatchingHwnds

        if wantedText and \
            not _normaliseText(wantedText) in _normaliseText(windowText):
                continue
        if wantedClass and \
            not windowClass == wantedClass:
                continue
        if selectionFunction and \
            not selectionFunction(childHwnd):
                continue
        results.append(childHwnd)
    return results


def explorer_fileselection():
    global clsid
    address_1=""
    files = []
    shellwindows = win32.Dispatch(clsid)
    w=win32gui
    window = w.GetForegroundWindow()
    #print("window: %s" % window)
    if (window != 0):
        if (w.GetClassName(window) == 'CabinetWClass'): # the main explorer window
            #print("class: %s" % w.GetClassName(window))
            #print("text: %s " %w.GetWindowText(window))
            children = list(set(searchChildWindows(window)))
            addr_edit = None
            file_view = None
            for child in children:
                if (w.GetClassName(child) == 'WorkerW'): # the address bar
                    addr_children = list(set(searchChildWindows(child)))
                    for addr_child in addr_children:
                        if (w.GetClassName(addr_child) == 'ReBarWindow32'):
                            addr_edit = addr_child
                            addr_children = list(set(searchChildWindows(child)))
                            for addr_child in addr_children:
                                if (w.GetClassName(addr_child) == 'Address Band Root'):
                                    addr_edit = addr_child
                                    addr_children = list(set(searchChildWindows(child)))
                                    for addr_child in addr_children:
                                        if (w.GetClassName(addr_child) == 'msctls_progress32'):
                                            addr_edit = addr_child
                                            addr_children = list(set(searchChildWindows(child)))
                                            for addr_child in addr_children:
                                                if (w.GetClassName(addr_child) == 'Breadcrumb Parent'):
                                                    addr_edit = addr_child
                                                    addr_children = list(set(searchChildWindows(child)))
                                                    for addr_child in addr_children:
                                                        if (w.GetClassName(addr_child) == 'ToolbarWindow32'):
                                                            text=getEditText(addr_child)
                                                            if "\\" in text:
                                                                address_1=getEditText(addr_child)[text.index(" ")+1:]
                                                                print("Address --> "+address_1)

    for window in range(shellwindows.Count):
        window_URL = urllib.parse.unquote(shellwindows[window].LocationURL,encoding='ISO 8859-1')
        window_dir = window_URL.split("///")[1].replace("/", "\\")
        print("Directory --> "+window_dir)
        if window_dir==address_1:
            selected_files = shellwindows[window].Document.SelectedItems()
            for file in range(selected_files.Count):
                files.append(selected_files.Item(file).Path)
            print("Files --> "+str(files))

while True:
    explorer_fileselection()
    time.sleep(1)

这将查找活动的Windows资源管理器窗口,获取该窗口的地址,然后将该地址用于Olav的回答,以检查该地址是否等于在Windows资源管理器中打开的地址之一,从而从活动的文件中获取文件窗口。顺便说一句,因为这是脚本,是两个答案的修改后的副本,所以它有这些限制。因此,就像奥拉夫(Olav)的回答“编辑:至少在使用上下文菜单时尚不起作用”一样,这也可能不会起作用,因为它是相同的代码-只是工作目录不同(尽管,我不知道他的意思,但是对于我测试过的方法,它确实有效。就像詹姆斯·肯特(James Kent)的回答一样,这不适用于桌面,仅适用于使用Windows资源管理器打开的窗口。 encoding ='ISO 8859-1'是因为我是葡萄牙语,但是可以更改,只需确保两个目录在没有%?s的情况下相等,否则将不起作用!

由于这个问题只有将近5年,OP可能不再需要它了,但是我需要它并且在任何地方都没有它,所以我想我可以在这里发布它,并且可以帮助其他想要做的人这个。脚本中的代码可用于了解当前Windows资源管理器窗口上的文件,以及获取XP以上版本的Windows(不确定Vista)上的当前Windows资源管理器窗口路径。对于XP,请参阅原始答案(https://stackoverflow.com/a/43892579/8228163),并要从所有Windows资源管理器窗口中获取文件,只需从Olav答案中删除if条件即可。

感谢Olav和James Kent的回答,因为我将花更多的时间试图找出方法(我是Python /任何一种语言的始作俑者-仅仅编码一年,所以本来需要非常多的时间,也许我必须将其与另一种语言混合使用。再次感谢OP,也感谢OP提出问题并让合适的人员在正确的时间回答! (因为Olav在链接上引用的来源已不存在。)

希望这会有所帮助!干杯!

答案 3 :(得分:0)

您是在询问如何获取文件列表,还是在询问如何进行转换?

如果您在询问选择文件(这对我来说是什么样的),您是在寻找GUI解决方案还是命令行解决方案?

你可以使用os.listdir()函数显示文件夹中的所有.xlsx文件是os库,然后将它们过滤到只包含.xlsx的文件,如下所示:

files = [ fi for fi in os.listdir(folder) if fi.endswith(suffix) ]

然后你可以在它们旁边打印带有索引的文件列表,并要求用户输入他们想要选择的文件的索引,如下所示:

for fInd,f in enumerate(files):
    print '%s) %s' %(fInd, f)

response = raw_input('select files by indices (coma separated)')
keeperInds = response.split(',')
keeperInds = [int(keeperInd) for keeperInd in keeperInds]
# you should also check to make sure that the selected inds are valid...
selectedFiles = [files[ind] for ind in keeperInds]

将为您提供可以传递到脚本中的所选文件的列表。

如果您真的需要有关从.xlsx文件到pdf的转换的帮助,您可以查看一下 - 您可以通过更改文件格式来更改它以保存.pdfs。 Converting .XLSX to .XLS in Python with win32com.client module