簡體   English   中英

Python:在資源管理器中獲取所選文件的列表(WIndows 7)

[英]Python: Get a list of selected files in Explorer (WIndows 7)

在工作中,我可以選擇多個.xlsx文件,右鍵單擊一個文件將為我提供組合文件以生成.pdf的選項。 現在我想在我的一個腳本中使用相同的功能。 也就是說,選擇多個文件並將這些文件的路徑作為參數發送到Python腳本。

我花了一個小時尋找解決方案,但我沒有找到任何好的答案。 似乎有一些C#答案,但我不知道如何將代碼轉換為Python。 是否有可能實現它?

編輯 - 示例腳本:

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

編輯:至少在使用上下文菜單時不起作用

我在這里找到了部分解決方案。 但是,如果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文件。

這實際上是一個Windows問題,並不是非常具體的Python。 您希望Windows shell在shell上下文菜單中顯示腳本的菜單項。

為此,您可以向注冊表添加一些鍵。 有關如何添加菜單項的說明,請參閱僅針對特定文件類型的“添加”菜單項到Windows上下文菜單

之后,當您選擇多個文件並將它們發送到腳本時,您將看到這些文件作為命令行參數。 如果選擇10個文件,腳本將運行10次。

我知道在這里發布一個答案有點“遲到”,但幾個月前我曾嘗試過Olav的解決方案而且它沒有完全發揮作用:工作目錄是腳本的工作目錄,所以我不得不刪除if的條件它工作,但它選擇了所有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的答案“編輯:還沒有工作,至少在使用上下文菜單時”,那么這也不會起作用,因為它是相同的代碼 - 它只是工作目錄不同(盡管如此,我不知道他的意思是什么,但是對於我測試過的,它確實有效)。 就像詹姆斯肯特的回答一樣,這不適用於桌面,只適用於使用Windows資源管理器打開的窗口。 編碼='ISO 8859-1'是因為我是葡萄牙語,但它可以改變,只是確保兩個目錄相等而沒有%?s或者不起作用!

由於這個問題只有近5年的時間,OP可能不再需要它,但是我需要它並且沒有它在任何地方,所以我想我可以在這里發布這個並且可能幫助其他想要這樣做的人。 腳本中的代碼可用於了解當前Windows資源管理器窗口中的文件,並獲取Windows上的當前Windows資源管理器窗口路徑(不確定Vista)。 對於XP,請參閱原始答案( https://stackoverflow.com/a/43892579/8228163 )並從所有Windows資源管理器窗口獲取文件,只需從Olav的答案中刪除if條件。

感謝Olav和James Kent的答案,因為我會花更多的時間試圖找到如何做到這一點(我是一個Python /任何語言的初學者 - 只需編碼一年,所以它會花費很多時間,也許我不得不把它和另一個laguage混在一起)。 再次感謝,也感謝OP提出問題並讓合適的人在合適的時間回答! (作為奧拉夫在鏈接上引用的來源不再存在)。

希望這可以幫助! 干杯!

您是在詢問如何獲取文件列表,還是在詢問如何進行轉換?

如果您在詢問選擇文件(這對我來說是什么樣的),您是在尋找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。 使用win32com.client模塊將.XLSX轉換為Python中的.XLS

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM