Как прочитать jpg или png из буфера обмена Windows в python и наоборот?

У меня есть изображение (jpg, png и т. д.) в буфере обмена Windows. Я хотел бы сохранить его в файл. win32clipboard кажется ответом, но каждый пример, который я могу найти, имеет дело с текстом.

скопируйте изображение в буфер обмена, затем

import win32clipboard
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData()
with open(name, 'wb') as f:
    f.write(data)
win32clipboard.CloseClipboard()

терпит неудачу с

TypeError: Specified clipboard format is not available

Я также хотел бы сделать обратное - получив файл изображения, записать его в буфер обмена.


person foosion    schedule 12.08.2011    source источник
comment
Если вы готовы использовать модуль wx, вы можете сделать это: stackoverflow.com/questions/2629907/   -  person Ian Burris    schedule 12.08.2011
comment
Я пытаюсь написать простую утилиту командной строки и в настоящее время не использую wx, поэтому я бы не стал туда идти.   -  person foosion    schedule 12.08.2011


Ответы (4)


Я бы просто использовал Pillow:

from PIL import ImageGrab
im = ImageGrab.grabclipboard()
im.save('somefile.png','PNG')
person Gerrat    schedule 12.08.2011
comment
Я только что смотрел на это, но мне нужны как python 2.7, так и 3.2, а PIL не доступен для 3.2. - person foosion; 13.08.2011
comment
@foosion: я думаю, что здесь есть предварительно скомпилированный двоичный файл для python 3.2: lfd.uci.edu/ ~gohlke/pythonlibs - person Gerrat; 13.08.2011
comment
Это очень просто для 2.7. Я надеялся, что добавление нескольких строк в версию win32clipboard будет работать так. - person foosion; 13.08.2011
comment
работает в 2.7. В 3.2 я получаю AttributeError: объект «байты» не имеет атрибута «сохранить» - person foosion; 13.08.2011
comment
@foosion: измените строку 68 в ImageGrab.py на if Image.isBytesType(data): или используйте обновленный установщик. - person cgohlke; 13.08.2011
comment
Есть ли противоположность ImageGrab — открыть файл изображения и скопировать изображение в буфер обмена? - person foosion; 13.08.2011
comment
Просто хотел упомянуть: в зависимости от того, как вы установили PIL, это может быть просто import ImageGrab вместо from PIL import ImageGrab. - person S. Kirby; 16.12.2012

Вам нужно передать параметр GetClipboardData, указав формат данных, которые вы ищете. Вы можете использовать EnumClipboardFormats, чтобы увидеть доступные форматы — когда я копирую что-то в проводнике, мне доступны 15 форматов.

Изменить 2: вот код для получения имени файла после того, как вы скопировали файл в проводнике. Ответ будет совершенно другим, если вы скопировали изображение из программы, например из браузера.

import win32clipboard
win32clipboard.OpenClipboard()
filename_format = win32clipboard.RegisterClipboardFormat('FileName')
if win32clipboard.IsClipboardFormatAvailable(filename_format):
    input_filename = win32clipboard.GetClipboardData(filename_format)
win32clipboard.CloseClipboard()

Редактировать 3: Из комментариев видно, что в буфере обмена находится фактическое изображение, а не имя файла изображения. Вы заявили, что не можете использовать PIL, поэтому:

import win32clipboard
win32clipboard.OpenClipboard()
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
    data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)
win32clipboard.CloseClipboard()

На данный момент у вас есть строка (в Python 2) или байты (в Python 3), содержащие данные изображения. Единственный формат, который вы сможете сохранить, — это .BMP, и вам придется декодировать BITMAPINFOHEADER, чтобы получить параметры для BITMAPFILEHEADER, который необходимо записать в начало файла.

person Mark Ransom    schedule 12.08.2011
comment
Я могу сам создать имя файла. Я циклически перебираю EnumClipboardFormats(), начиная с 0? Что мне делать с результатами? - person foosion; 13.08.2011
comment
@foosion, не могли бы вы уточнить, что именно находится в буфере обмена? Вы сделали копию изображения, отображаемого в программе, или имени файла в Проводнике? - person Mark Ransom; 13.08.2011
comment
Мои текущие тесты заключаются в копировании изображения со страницы в веб-браузере и из программы рисования. Также, пожалуйста, смотрите мой комментарий к bgporter - person foosion; 13.08.2011
comment
Марк, боюсь, я мог быть неясным. Я не нажимаю на имя файла в проводнике, я нажимаю на изображение и копирую изображение в буфер обмена. - person foosion; 13.08.2011
comment
@foosion, в вашем первоначальном вопросе говорилось, что у меня есть файл изображения, что привело меня в замешательство. Ваш комментарий выше внес ясность. - person Mark Ransom; 13.08.2011
comment
Первый код дает мне такие пути, как C:\Users\hamza\Desktop\ATTACH~1\FA1729~1.PNG, где фактический путь C:\Users\hamza\Desktop\attachments\facetache (3). Как получить фактические пути, используя это? - person Hamza Khurshid; 31.01.2020
comment
@HamzaKhurshid, вы сможете открыть файл с таким именем — это сокращенная версия, которую Windows использует внутри. Если вам действительно нужно преобразовать это имя файла в длинную форму, это станет отличным новым вопросом. - person Mark Ransom; 31.01.2020
comment
Я не могу открыть, используя это имя файла. Я попытался преобразовать его в полный путь, перечислив все файлы каталога и выбрав. Мне немного удалось, но потерпел неудачу в случае файлов с именами, начинающимися с одинаковых символов. - person Hamza Khurshid; 31.01.2020
comment
Решение найдено. Короткий путь можно превратить в длинный, используя win32api.GetLongPathName(shortPath) - person Hamza Khurshid; 31.01.2020

Использование PythonMagick (двоичные файлы):

from PythonMagick import Image
Image("clipboard:").write("PNG32:clipboard.png")  # clipboard -> file
Image("clipboard.png").write("clipboard:")  # file -> clipboard
person cgohlke    schedule 12.08.2011
comment
Это выглядит многообещающе и даже поддерживает как clip-›file, так и file-›clip. - person foosion; 13.08.2011

Функция win32clipboard.GetClipboardData() имеет параметр. Параметр по умолчанию указывает, что вы хотите, чтобы содержимое буфера обмена было текстовым. Вам нужно передать значение, указывающее формат данных, который вы хотите, чтобы буфер обмена предоставил вам.

Стандартные форматы данных буфера обмена задокументированы здесь< /а>.

ТАКЖЕ:

См. здесь документацию по EnumClipboardFormats() — в основном , вам нужен такой код (непроверенный), чтобы получить форматы, которые в настоящее время доступны в буфере обмена:

formats = []
lastFormat = 0
while 1:
    nextFormat = win32clipboard.EnumClipboardFormats(lastFormat)
    if 0 == nextFormat:
         # all done -- get out of the loop
         break
    else:
         formats.append(nextFormat)
         lastFormat = nextFormat
# when you get here, formats contains a list of format codes that
# you can retrieve from the clipboard right now.
person bgporter    schedule 12.08.2011
comment
Я только что попробовал перечислить форматы и получил 49353, 0, 8, 0, 0, 0, 0, 17, 0. Мне просто использовать 49353? win32clipboard.OpenClipboard(49353) приводит к ошибке: (1400, «OpenClipboard», «Недопустимый дескриптор окна».) - person foosion; 13.08.2011
comment
Вы видели страницу с документацией о том, что такое коды форматов? 8 — это BITMAPINFO, а 7 — это BITMAPV5HEADER, оба из которых включают данные растрового изображения. В конечном итоге вам может понадобиться больше узнать о том, как Windows обрабатывает изображения, чем вы хотите... - person bgporter; 13.08.2011
comment
Ни один из кодов, перечисленных в msdn.microsoft.com /en-us/library/ff729168(v=VS.85).aspx достигает 49353. Следующее значение — 8, это структура BITMAINFO, но я не понимаю, что с ней делать - person foosion; 13.08.2011
comment
49353 не является стандартным форматом — в Win32 есть функция GetClipboardFormatName, которая должна дать вам представление о том, что содержит этот формат. - person bgporter; 13.08.2011
comment
@foosion, любой формат 49152 или выше не является одним из жестко запрограммированных форматов, поэтому он может варьироваться от системы к системе. Как говорит bgporter, используйте GetClipboardFormatName, чтобы узнать, что это такое. - person Mark Ransom; 13.08.2011
comment
К первому комментарию: Код формата должен быть указан как параметр для GetClipboardData(), а не для OpenClipboard(). - person alexis; 10.04.2013