Скопируйте изображение PIL / PILLOW в буфер обмена Windows

Я видел этот вопрос, и я следил за каждым шагом, изменяя код в соответствии со своими требованиями, а именно Python3, Pillow и ctypes. Чем меньше библиотек, тем лучше.

import ctypes
from PIL import ImageGrab, Image
from io import BytesIO

user32 = ctypes.windll.user32

img = ImageGrab.grab()
output = BytesIO()
img.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()

user32.OpenClipboard()
user32.EmptyClipboard()
user32.SetClipboardData(user32.CF_DIB, data)
user32.CloseClipboard()

Это вырезанный код из моего скрипта, который, я думаю, является тем же кодом, что и вопрос, перенесенный на мои требования. При выполнении он должен скопировать текущий рабочий стол в буфер обмена. Вместо этого я получаю это:

File "C:\Users\Gcq\Documents\python\Screen\Screen.py", line 132, in shot
    user32.OpenClipboard()
ValueError: Procedure probably called with not enough arguments (4 bytes missing)

Мне очень жаль, что я задаю здесь такой (вероятно) простой вопрос, но я действительно не знаю, что именно не получается, а ctypes - не мое.


person gcq    schedule 23.01.2014    source источник


Ответы (2)


Уф. Очевидно, библиотека win32clipboard действительно упрощает некоторые вещи по сравнению с ctypes. Ваша попытка просто заменить одно другим далеко не верна.

Итак, я загрузил свою виртуальную машину Windows, установил Pillow и переписал вашу программу, обучаясь у двух других answers:

import io

import ctypes
msvcrt = ctypes.cdll.msvcrt
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32

from PIL import ImageGrab

img = ImageGrab.grab()
output = io.BytesIO()
img.convert('RGB').save(output, 'BMP')
data = output.getvalue()[14:]
output.close()

CF_DIB = 8
GMEM_MOVEABLE = 0x0002

global_mem = kernel32.GlobalAlloc(GMEM_MOVEABLE, len(data))
global_data = kernel32.GlobalLock(global_mem)
msvcrt.memcpy(ctypes.c_char_p(global_data), data, len(data))
kernel32.GlobalUnlock(global_mem)
user32.OpenClipboard(None)
user32.EmptyClipboard()
user32.SetClipboardData(CF_DIB, global_mem)
user32.CloseClipboard()
person Oleh Prypin    schedule 23.01.2014
comment
+1. В более общем смысле, просто слепой вызов функций из Win32 DLL - плохая идея. По крайней мере, на самом деле он не будет падать так часто, как когда вы это делаете в POSIX ... но вам нужно посмотреть функцию в MSDN, отправить правильные параметры и часто установить argtypes и / или restype, прежде чем вы сможете ее использовать. - person abarnert; 24.01.2014
comment
Ага, теперь я чувствую себя глупо. Оно работает! Теперь у меня другая проблема: я не могу вставить изображение. По крайней мере, Word это не принимает. - person gcq; 24.01.2014
comment
@gcq Все еще пытаюсь понять. Видимо это намного сложнее, чем вы думали. - person Oleh Prypin; 24.01.2014
comment
global_data - указатель, как и дескриптор global_mem (на внутреннюю структуру данных; это не маленький индекс в таблице дескрипторов). Для них вам нужно объявить restype, иначе ваш код неверен в Win64. ctypes по умолчанию приводит числа к C int. Кроме того, если вы не выполняете перенос вручную с помощью c_void_p, вам также необходимо установить argtypes. - person Eryk Sun; 24.01.2014
comment
Хм, я точно не специалист в этом. @gcq, вам, вероятно, следует использовать другой ответ (теперь он исправлен для Python 3). - person Oleh Prypin; 24.01.2014

В примере используется pywin32, оболочка Python для Win32 API, скрывающая некоторые детали низкого уровня, о которых вам нужно позаботиться самостоятельно, если вы хотите использовать ctypes.

Вот как вы это делаете, используя ctypes, он добавляет функциональную возможность создания глобально выделенного буфера и копирования data в этот буфер:

#!python

from PIL import Image
#from cStringIO import StringIO
from io import BytesIO
from ctypes import *
from ctypes.wintypes import *

HGLOBAL = HANDLE
SIZE_T = c_size_t
GHND = 0x0042
GMEM_SHARE = 0x2000

GlobalAlloc = windll.kernel32.GlobalAlloc
GlobalAlloc.restype = HGLOBAL
GlobalAlloc.argtypes = [UINT, SIZE_T]

GlobalLock = windll.kernel32.GlobalLock
GlobalLock.restype = LPVOID
GlobalLock.argtypes = [HGLOBAL]

GlobalUnlock = windll.kernel32.GlobalUnlock
GlobalUnlock.restype = BOOL
GlobalUnlock.argtypes = [HGLOBAL]

CF_DIB = 8

OpenClipboard = windll.user32.OpenClipboard
OpenClipboard.restype = BOOL 
OpenClipboard.argtypes = [HWND]

EmptyClipboard = windll.user32.EmptyClipboard
EmptyClipboard.restype = BOOL
EmptyClipboard.argtypes = None

SetClipboardData = windll.user32.SetClipboardData
SetClipboardData.restype = HANDLE
SetClipboardData.argtypes = [UINT, HANDLE]

CloseClipboard = windll.user32.CloseClipboard
CloseClipboard.restype = BOOL
CloseClipboard.argtypes = None

#################################################

image = Image.new("RGB", (200, 200), (255, 0, 0))

#output = StringIO()
output = BytesIO()
image.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()

hData = GlobalAlloc(GHND | GMEM_SHARE, len(data))
pData = GlobalLock(hData)
memmove(pData, data, len(data))
GlobalUnlock(hData)

OpenClipboard(None)
EmptyClipboard()
SetClipboardData(CF_DIB, pData)
CloseClipboard()
person Community    schedule 23.01.2014