Python: получить список выбранных файлов в проводнике (WIndows 7)

На работе я могу выбрать несколько файлов .xlsx, и щелчок правой кнопкой мыши по одному файлу даст мне возможность объединить файлы в .pdf. Теперь я хочу использовать ту же функциональность в одном из моих скриптов. То есть выбор нескольких файлов и отправка путей к этим файлам в качестве аргументов скрипту Python.

Вскоре я потратил час на поиски решений, но не нашел хороших ответов. Кажется, есть несколько ответов на C #, но я не знаю, как преобразовать код в Python. Возможно ли вообще этого добиться?

Изменить - Пример сценария:

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

person Olav    schedule 20.01.2014    source источник
comment
@John Я предлагаю stackoverflow.com/questions/2123762/, возможно, более тесно связан.   -  person dss539    schedule 20.01.2014


Ответы (4)


Я знаю, что сейчас "немного" поздно публиковать здесь ответ, но я попробовал решение Олава несколько месяцев назад, и оно не сработало полностью: рабочий каталог был рабочим каталогом сценария, поэтому мне пришлось удалить условие if для он работал, но он выбрал все файлы во всех окнах проводника Windows (что я тоже хотел, так что это сработало частично для меня). Но теперь я вернулся, чтобы продолжить свой проект (помощник), и я обнаружил, что мне действительно нужна эта работа, поэтому я подумал над этой идеей (которую не так уж сложно придумать, но мне потребовались месяцы, чтобы она появилась ... ). Я не знаю, сработал ли этот ответ для кого-то еще, но для меня это не было полностью, поэтому я подумал, что могу улучшить его и опубликовать свое решение здесь. Этот код представляет собой смесь этого ответа (который я тоже использовал в том же скрипте, но никогда не думал о том, чтобы заставить их работать вместе): https://stackoverflow.com/a/43892579/8228163 (исправлено мной, чтобы работать как минимум под Windows 7), и ответ Олава и результат сработали для меня - скрипт обнаруживает файлы только в текущем Окно проводника 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, получает адрес этого окна, а затем адрес используется в ответе Олафа, чтобы проверить, совпадает ли этот адрес с одним из адресов, открытых в проводнике Windows, получая файлы из активного окна. Кстати, поскольку это скрипт является измененной копией обоих ответов, он имеет ограничения от них. Итак, как и в ответе Олафа «Изменить: еще не работает, по крайней мере, при использовании контекстного меню», то это, вероятно, тоже не сработает, поскольку это тот же код - это просто рабочий каталог, который отличается (хотя, Я не знаю, что он имел в виду, но для того, что я тестировал, это сработало). И, как и в ответе Джеймса Кента, это не работает для рабочего стола, только для открытых окон с помощью проводника Windows. Кодировка = 'ISO 8859-1' связана с тем, что я португальский, но ее можно изменить, ПРОСТО убедитесь, что оба каталога равны без%? S, иначе это не сработает!

Поскольку на этот вопрос всего почти 5 лет, OP, вероятно, он больше не понадобится, но он мне был нужен, и его нигде не было, поэтому я подумал, что могу опубликовать это здесь и, возможно, помочь другим, кто хочет это сделать. Код в сценарии можно использовать для определения файлов в текущем окне проводника Windows и для получения текущего пути к окну проводника Windows в Windows выше XP (не уверен в Vista). Для XP см. Исходный ответ (https://stackoverflow.com/a/43892579/8228163) и получить файлы из всех окон проводника Windows, просто удалите условие if из ответа Олафа.

Спасибо Олаву и Джеймсу Кенту за ответы, потому что я бы потратил НАМНОГО больше времени, пытаясь выяснить, как это сделать (я начинаю с Python / любого языка - просто кодирую в течение года, так что это заняло бы действительно много времени , может, придется перепутать с другим языком). Еще раз спасибо, а также OP за то, что задали вопросы и получили ответы от нужных людей в нужное время! (поскольку источник, который процитировал Олав по ссылке, больше не существует).

Надеюсь это поможет! Ваше здоровье!

person DADi590    schedule 24.10.2018
comment
Спасибо, что нашли время ответить на ваше решение. Я бегло просмотрел то, что вы написали; Я надеюсь, что скоро смогу это проверить. - person Olav; 26.10.2018
comment
пожалуйста! кстати, только что увидел, что ты ОП, хахаха. не видел этого, когда писал ответ, так что это звучит так, как будто я писал для кого-то другого, и вы были одним из ответов, а не OP ... извините за это :-). в любом случае, надеюсь, это поможет! - person DADi590; 26.10.2018
comment
Еще один момент: вы помните, что вы имели в виду под «Еще не работает», по крайней мере, при использовании контекстного меню? Для чего вам понадобилось контекстное меню? - person DADi590; 26.10.2018

Изменить: пока не работает, по крайней мере, при использовании контекстного меню

Я нашел частичное решение здесь. Однако это не работает, если открыт 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.

person Olav    schedule 21.01.2014

Это действительно вопрос Windows и не очень специфичен для Python. Вы хотите, чтобы оболочка Windows отображала пункт меню для вашего сценария в контекстном меню оболочки.

Для этого вы можете добавить несколько ключей в реестр. См. Добавить пункт меню в контекстное меню Windows только для конкретный тип файла, чтобы узнать, как добавить пункт меню.

После этого, когда вы выберете несколько файлов и отправите их в свой сценарий, вы увидите файлы как аргументы командной строки. Если вы выберете 10 файлов, скрипт будет запущен 10 раз.

person dss539    schedule 20.01.2014
comment
У меня создалось впечатление, что с помощью этого метода будут запущены 10 экземпляров моего скрипта. Но сейчас я попробую, спасибо! - person Olav; 20.01.2014
comment
@Olav Ну .. Надеюсь, что нет. Если да, то это значит, что моя память не так хороша. - person dss539; 20.01.2014
comment
Я сделал образец сценария (см. Редактирование), и он открывает два экземпляра Python при тестировании двух файлов определенного типа. - person Olav; 20.01.2014
comment
@Olav Я отредактировал свой ответ, чтобы отразить тот факт, что он будет запускаться 10 раз. Спасибо что подметил это. Прости. - person dss539; 21.01.2014

Вы спрашиваете, как получить список файлов, или спрашиваете, как выполнить преобразование?

Если вы спрашиваете о выборе файлов (как мне кажется), ищете ли вы решение с графическим интерфейсом или решение для командной строки?

вы можете отобразить все файлы .xlsx в папке с помощью функции os.listdir (), являющейся библиотекой ОС, а затем отфильтровать их только до файлов, содержащих .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, вы можете взглянуть на это - вы можете изменить его для сохранения .pdf, изменив формат файла. Преобразование .XLSX в .XLS в Python с помощью win32com.client модуль

person NotNamedDwayne    schedule 20.01.2014
comment
Мне нужен список файлов с помощью проводника Windows. Я знаю, что могу использовать командную строку или какое-либо решение с графическим интерфейсом пользователя с askopenfilenames, но я действительно хочу использовать то же решение, что и на работе, с помощью Explorer. - person Olav; 20.01.2014
comment
звучит так, будто вы не хотите использовать python, тогда - person NotNamedDwayne; 21.01.2014