Issue
The main answer from How to paste a Numpy array to Excel works to copy a Numpy array in the clipboard, ready to be pasted in Excel:
import numpy as np
import win32clipboard as clipboard
def toClipboardForExcel(array):
array_string = "\r\n".join("\t".join(line.astype(str)).replace("\n","") for line in array)
clipboard.OpenClipboard()
clipboard.EmptyClipboard()
clipboard.SetClipboardText(array_string)
clipboard.CloseClipboard()
Y = np.arange(64).reshape((8, 8))
toClipboardForExcel(Y)
Is it possible to do this without the extra win32
package dependency? (I don't use it currently in my project and I'd like to avoid adding it only for clipboard, NB: I don't use pandas
either)
I already tried with os.system(`echo string | clip`)
but it doesn't work for multiline content (containing \n
).
Or maybe is OpenClipboard
, SetClipboardText
, etc. accessible via ctypes
(which I already use)?
NB: this is not a duplicate of Python script to copy text to clipboard because the latter is general, and with extra dependencies, and here in my question, we would like to avoid new dependencies.
Solution
Here is a ctypes
-only example to set any Python str
-type text on the clipboard and read it back. Note that text should not contain nulls as the CF_UNICODETEXT
type expects to be null-terminated text.
import ctypes as ct
import ctypes.wintypes as w
CF_UNICODETEXT = 13
NO_ERROR = 0
SIZE_T = ct.c_size_t
GMEM_MOVEABLE = 0x0002
# Error handlers to raise exceptions on failure.
def boolcheck(result, func, args):
if not result:
raise ct.WinError(ct.get_last_error())
def nullcheck(result, func, args):
if result is None:
raise ct.WinError(ct.get_last_error())
return result
def zeroerrorcheck(result, func, args):
if not result:
err = ct.get_last_error()
if err != NO_ERROR:
raise ct.WinError(err)
return result
# Capture GetLastError() code after each call.
# Fully specify argtypes and restype for ctypes type-checking.
kernel32 = ct.WinDLL('kernel32', use_last_error=True)
GlobalLock = kernel32.GlobalLock
GlobalLock.argtypes = w.HGLOBAL,
GlobalLock.restype = w.LPVOID
GlobalLock.errcheck = nullcheck
GlobalAlloc = kernel32.GlobalAlloc
GlobalAlloc.argtypes = w.UINT, SIZE_T
GlobalAlloc.restype = w.HGLOBAL
GlobalAlloc.errcheck = nullcheck
GlobalUnlock = kernel32.GlobalUnlock
GlobalUnlock.argtypes = w.HGLOBAL,
GlobalUnlock.restype = w.BOOL
GlobalUnlock.errcheck = zeroerrorcheck
user32 = ct.WinDLL('user32', use_last_error=True)
OpenClipboard = user32.OpenClipboard
OpenClipboard.argtypes = w.HWND,
OpenClipboard.restype = w.BOOL
OpenClipboard.errcheck = boolcheck
GetClipboardData = user32.GetClipboardData
GetClipboardData.argtypes = w.UINT,
GetClipboardData.restype = w.HANDLE
GetClipboardData.errcheck = nullcheck
SetClipboardData = user32.SetClipboardData
SetClipboardData.argtypes = w.UINT, w.HANDLE
SetClipboardData.restype = w.HANDLE
SetClipboardData.errcheck = nullcheck
CloseClipboard = user32.CloseClipboard
CloseClipboard.argtypes = ()
CloseClipboard.restype = w.BOOL
CloseClipboard.errcheck = boolcheck
EmptyClipboard = user32.EmptyClipboard
EmptyClipboard.argtypes = ()
EmptyClipboard.restype = w.BOOL
EmptyClipboard.errcheck = boolcheck
GetForegroundWindow = user32.GetForegroundWindow
GetForegroundWindow.argtypes = ()
GetForegroundWindow.restype = w.HWND
def get_clipboard_text():
OpenClipboard(GetForegroundWindow())
hmem = GetClipboardData(CF_UNICODETEXT)
pmem = GlobalLock(hmem)
text = ct.wstring_at(pmem)
GlobalUnlock(hmem)
CloseClipboard()
return text
def set_clipboard_text(text):
ztext = text + '\x00' # null terminator required
OpenClipboard(None)
EmptyClipboard()
hmem = GlobalAlloc(GMEM_MOVEABLE, len(ztext) * ct.sizeof(w.WCHAR))
pmem = GlobalLock(hmem)
btext = ztext.encode('utf-16le')
ct.memmove(pmem, btext, len(btext))
GlobalUnlock(hmem)
SetClipboardData(CF_UNICODETEXT, hmem)
CloseClipboard()
set_clipboard_text('马克')
print(get_clipboard_text())
Per OpenClipboard
documentation remarks:
If an application calls OpenClipboard with hwnd set to NULL,
EmptyClipboard
sets the clipboard owner to NULL; this causesSetClipboardData
to fail.
My experience is that using NULL (None
in Python) with OpenClipboard
does not cause SetClipboardData
to fail, but doesn't prevent other apps using the clipboard while open (potential race condition?). When a valid Window handle is used, other apps will fail to open the clipboard until CloseClipboard
is called, so some retrying and error checking will be needed. Probably best to use a valid owner. I've used GetForegroundWindow
above.
Answered By - Mark Tolonen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.