如何使用Python在OS剪贴板中添加/获取图像数据?
我有一些Python代码可以编辑图像文件,我想知道如何把图像数据放到操作系统的剪贴板上,并且从剪贴板中获取这些数据。
在寻找一个跨平台的解决方案来替换或获取剪贴板中的文本时,发现有很多简单的方法可以做到这一点(比如使用内置的Tkinter模块,配合一些代码)。
但是,这些方法只能处理纯文本,不能处理其他剪贴板数据,比如图像。
我使用的Python版本是3.x,操作系统是Windows,但我希望这个解决方案是跨平台的(可以在不同的操作系统上运行),而且也要支持其他Python版本,比如2.x。
我觉得应该只用Python内置的模块,代码也不应该太复杂(或者需要有解释说明它的功能)。可以做成一个Python模块,因为文件可以和可移植的程序代码放在同一个文件夹里,这样就不用安装了。
还有一些和这个问题相关的其他问题,可能适用于图像,但它们只支持特定的操作系统。比较好的有在Python3中复制图像到剪贴板和用PIL和win32clipboard在Python中写入图像到Windows剪贴板。
这些方法(仅适用于Windows)似乎使用了以下步骤:
- 获取图像的原始二进制数据 - 这个方法使用Python图像库(PIL/Pillow)加载图像文件,因为它有其他后续处理功能,使用起来简单且流行。这也可以用其他模块来实现(比如Pygame)。
- 创建一个文件对象变量(用于内存中的输入/输出流),使用内置的io模块。对于Python 2.x,使用
from cStringIO import StringIO
,而在Python 3中,使用更好的io.BytesIO
二进制流对象类型 - 旧的只支持文本。 - 将图像数据以BMP(Windows位图/设备无关位图)文件格式保存到上一步的文件对象变量中。使用PIL/Pillow的方法首先通过
.convert("RGB")
将数据转换到包含它的变量中。 - 获取文件对象变量内存缓冲区的完整内容作为二进制数据(
bytes
对象),从位置14开始切片,以去掉BMP/DIB文件格式的14字节头,然后将其保存为一个变量。这个方法说这种数据切片在32位或64位系统上都有效,但需要Windows剪贴板API,所以不适用于其他文件格式。 - 关闭内存缓冲区,并将上一步的图像数据复制到剪贴板。这个方法在Windows上使用
win32clipboard
扩展模块来实现 - 它打开剪贴板以供使用,清空剪贴板,将其值设置为来自上一步的图像数据变量(使用BMP/DIB类型),然后关闭打开的剪贴板。
另外,还有一个简单的跨平台剪贴板文本模块叫做Pyperclip,它只有一个文件,版本是1.5.6,可能有处理图像数据的代码。
5 个回答
正如马丁所说——
Pyperclip绝对是个不错的选择,使用起来非常顺手。
我不知道为什么你不应该使用它。
它的用法简单到只需要下面的三行代码,
import pyperclip
pyperclip.copy('The text to be copied to the clipboard.')
paste= pyperclip.paste()
这里有一个基于这个回答的Python函数,它可以用
def use_clipboard(paste_text=None):
import tkinter # For Python 2, replace with "import Tkinter as tkinter".
tk = tkinter.Tk()
tk.withdraw()
if type(paste_text) == str: # Set clipboard text.
tk.clipboard_clear()
tk.clipboard_append(paste_text)
try:
clipboard_text = tk.clipboard_get()
except tkinter.TclError:
clipboard_text = ''
r.update() # Stops a few errors (clipboard text unchanged, command line program unresponsive, window not destroyed).
tk.destroy()
return clipboard_text
这个方法会创建一个快速隐藏的窗口,然后很快关闭,所以这应该不是个问题。此外,它只支持在剪贴板中使用纯文本,而不支持我在上面问题中提到的图片。
xerox
库既简单又强大。
问题: 如何使用Python在操作系统的剪贴板中添加或获取图片数据?
这里我只展示获取的部分:
这个例子使用了内置的Tkinter模块来从CLIPBOARD
获取图片数据。
只在Linux上测试过,但应该可以在不同平台上使用。
注意:显示的387x388 GIF的第一次
粘贴
需要4秒。
核心要点: 你需要使用MIME类型来请求图片。
.clipboard_get(type='image/png')
经过验证,使用'GIF'
、'PNG'
和'JPEG'
作为源图片数据,应用程序包括GIMP
和PyCharm
。使用type='image/png'
时,如果源应用程序支持,你总是能获取到'PNG'
类型的图片数据。
参考资料:
clippboard_get(type=<string>)
从剪贴板中获取数据。类型指定数据返回的形式,应该是像STRING或FILE_NAME这样的原子名称。在现代X11系统中,类型默认是UTF8_STRING。
数据格式:
0x89 0x50 0x4e 0x47 0xd 0xa 0x1a 0xa 0x0 0x0 0x0 0xd 0x49 0x48 0x44
数据被分成用空格分隔的字段;每个字段被转换为其原子值,传输的是32位的原子值,而不是原子名称。
在去掉空格并用int(<field>, 0)
转换后:
bytearray(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHD...')
<PIL.PngImagePlugin.PngImageFile image mode=P size=387x388 at 0xF555E20C>
异常情况: 如果没有任何选择,或者源应用程序不提供'image/png'
。
TclError:CLIPBOARD selection doesn't exist or form "image/png" not defined
import tkinter as tk
from PIL import Image, ImageTk
import io
class App(tk.Tk):
def __init__(self):
super().__init__() # options=(tk.Menu,))
self.menubar = tk.Menu()
self.config(menu=self.menubar)
self.menubar.add_command(label='paste', command=self.on_paste)
self.label = tk.Label(self, text="CLIPBOARD image", font=("David", 18),
image='', compound='center')
self.label.grid(row=0, column=0, sticky='w')
def on_paste(self):
self.label.configure(image='')
self.update_idletasks()
try:
b = bytearray()
h = ''
for c in self.clipboard_get(type='image/png'):
if c == ' ':
try:
b.append(int(h, 0))
except Exception as e:
print('Exception:{}'.format(e))
h = ''
else:
h += c
except tk.TclError as e:
b = None
print('TclError:{}'.format(e))
finally:
if b is not None:
with Image.open(io.BytesIO(b)) as img:
print('{}'.format(img))
self.label.image = ImageTk.PhotoImage(img.resize((100, 100), Image.LANCZOS))
self.label.configure(image=self.label.image)
在Python 3.5中测试:'TclVersion': 8.6 'TkVersion': 8.6