How do I read text from the (windows) clipboard with python?
dreftymac
30.8k26 gold badges117 silver badges180 bronze badges
asked Sep 19, 2008 at 11:09
2
You can use the module called win32clipboard, which is part of pywin32.
Here is an example that first sets the clipboard data then gets it:
import win32clipboard
# set clipboard data
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText('testing 123')
win32clipboard.CloseClipboard()
# get clipboard data
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()
print data
An important reminder from the documentation:
When the window has finished examining or changing the clipboard,
close the clipboard by calling CloseClipboard. This enables other
windows to access the clipboard. Do not place an object on the
clipboard after calling CloseClipboard.
SuperStormer
4,8625 gold badges23 silver badges34 bronze badges
answered Sep 19, 2008 at 11:20
SakinSakin
3,2973 gold badges23 silver badges27 bronze badges
5
you can easily get this done through the built-in module Tkinter which is basically a GUI library. This code creates a blank widget to get the clipboard content from OS.
from tkinter import Tk # Python 3
#from Tkinter import Tk # for Python 2.x
Tk().clipboard_get()
answered May 24, 2014 at 11:58
kmonsoorkmonsoor
7,2807 gold badges42 silver badges55 bronze badges
1
I found pyperclip to be the easiest way to get access to the clipboard from python:
-
Install pyperclip:
pip install pyperclip
-
Usage:
import pyperclip
s = pyperclip.paste()
pyperclip.copy(s)
# the type of s is string
With supports Windows, Linux and Mac, and seems to work with non-ASCII characters, too.
Tested characters include ±°©©αβγθΔΨΦåäö
answered Jul 3, 2016 at 15:54
np8np8
25.2k10 gold badges86 silver badges93 bronze badges
6
If you don’t want to install extra packages, ctypes
can get the job done as well.
import ctypes
CF_TEXT = 1
kernel32 = ctypes.windll.kernel32
kernel32.GlobalLock.argtypes = [ctypes.c_void_p]
kernel32.GlobalLock.restype = ctypes.c_void_p
kernel32.GlobalUnlock.argtypes = [ctypes.c_void_p]
user32 = ctypes.windll.user32
user32.GetClipboardData.restype = ctypes.c_void_p
def get_clipboard_text():
user32.OpenClipboard(0)
try:
if user32.IsClipboardFormatAvailable(CF_TEXT):
data = user32.GetClipboardData(CF_TEXT)
data_locked = kernel32.GlobalLock(data)
text = ctypes.c_char_p(data_locked)
value = text.value
kernel32.GlobalUnlock(data_locked)
return value
finally:
user32.CloseClipboard()
print(get_clipboard_text())
answered Apr 25, 2014 at 5:54
kichikkichik
32.1k6 gold badges92 silver badges110 bronze badges
5
The most upvoted answer above is weird in a way that it simply clears the Clipboard and then gets the content (which is then empty). One could clear the clipboard to be sure that some clipboard content type like «formated text» does not «cover» your plain text content you want to save in the clipboard.
The following piece of code replaces all newlines in the clipboard by spaces, then removes all double spaces and finally saves the content back to the clipboard:
import win32clipboard
win32clipboard.OpenClipboard()
c = win32clipboard.GetClipboardData()
win32clipboard.EmptyClipboard()
c = c.replace('n', ' ')
c = c.replace('r', ' ')
while c.find(' ') != -1:
c = c.replace(' ', ' ')
win32clipboard.SetClipboardText(c)
win32clipboard.CloseClipboard()
answered Jun 19, 2012 at 8:00
bornborn
5651 gold badge4 silver badges18 bronze badges
The python standard library does it…
try:
# Python3
import tkinter as tk
except ImportError:
# Python2
import Tkinter as tk
def getClipboardText():
root = tk.Tk()
# keep the window from showing
root.withdraw()
return root.clipboard_get()
ankostis
8,1123 gold badges45 silver badges61 bronze badges
answered Apr 4, 2018 at 8:42
2
For my console program the answers with tkinter above did not quite work for me because the .destroy() always gave an error,:
can’t invoke «event» command: application has been destroyed while executing…
or when using .withdraw() the console window did not get the focus back.
To solve this you also have to call .update() before the .destroy(). Example:
# Python 3
import tkinter
r = tkinter.Tk()
text = r.clipboard_get()
r.withdraw()
r.update()
r.destroy()
The r.withdraw() prevents the frame from showing for a milisecond, and then it will be destroyed giving the focus back to the console.
answered Jan 17, 2015 at 1:08
user136036user136036
10.5k6 gold badges45 silver badges46 bronze badges
Use Pythons library Clipboard
Its simply used like this:
import clipboard
clipboard.copy("this text is now in the clipboard")
print clipboard.paste()
answered Apr 27, 2016 at 10:19
DanDan
1471 silver badge4 bronze badges
3
After whole 12 years, I have a solution and you can use it without installing any package.
from tkinter import Tk, TclError
from time import sleep
while True:
try:
clipboard = Tk().clipboard_get()
print(clipboard)
sleep(5)
except TclError:
print("Clipboard is empty.")
sleep(5)
answered Apr 29, 2021 at 19:17
A not very direct trick:
Use pyautogui hotkey:
Import pyautogui
pyautogui.hotkey('ctrl', 'v')
Therefore, you can paste the clipboard data as you like.
answered Jul 9, 2019 at 7:06
1
import pandas as pd
df = pd.read_clipboard()
answered Dec 7, 2021 at 4:21
AthiiAthii
952 silver badges11 bronze badges
1
Why not try calling powershell?
import subprocess
def getClipboard():
ret = subprocess.getoutput("powershell.exe -Command Get-Clipboard")
return ret
answered Feb 18, 2022 at 5:36
1
For users of Anaconda: distributions don’t come with pyperclip, but they do come with pandas which redistributes pyperclip:
>>> from pandas.io.clipboard import clipboard_get, clipboard_set
>>> clipboard_get()
'from pandas.io.clipboard import clipboard_get, clipboard_set'
>>> clipboard_set("Hello clipboard!")
>>> clipboard_get()
'Hello clipboard!'
I find this easier to use than pywin32 (which is also included in distributions).
answered Dec 6, 2021 at 16:14
asdf101asdf101
5295 silver badges18 bronze badges
I just need a python script that copies text to the clipboard.
After the script gets executed i need the output of the text to be pasted to another source.
Is it possible to write a python script that does this job?
asked Jun 16, 2012 at 12:32
3
See Pyperclip. Example (taken from Pyperclip site):
import pyperclip
pyperclip.copy('The text to be copied to the clipboard.')
spam = pyperclip.paste()
Also, see Xerox. But it appears to have more dependencies.
vauhochzett
2,2442 gold badges17 silver badges36 bronze badges
answered Jun 16, 2012 at 12:35
robertrobert
32.4k8 gold badges52 silver badges72 bronze badges
4
On macOS, use subprocess.run
to pipe your text to pbcopy
:
import subprocess
data = "hello world"
subprocess.run("pbcopy", text=True, input=data)
It will copy «hello world» to the clipboard.
answered Jun 28, 2013 at 18:27
kyle kkyle k
4,9048 gold badges30 silver badges45 bronze badges
5
To use native Python directories, use:
import subprocess
def copy2clip(txt):
cmd='echo '+txt.strip()+'|clip'
return subprocess.check_call(cmd, shell=True)
on Mac, instead:
import subprocess
def copy2clip(txt):
cmd='echo '+txt.strip()+'|pbcopy'
return subprocess.check_call(cmd, shell=True)
Then use:
copy2clip('This is on my clipboard!')
to call the function.
answered Dec 8, 2016 at 0:47
BinyaminBinyamin
6098 silver badges17 bronze badges
8
PyQt5:
from PyQt5.QtWidgets import QApplication
import sys
def main():
app = QApplication(sys.argv)
cb = QApplication.clipboard()
cb.clear(mode=cb.Clipboard )
cb.setText("Copy to ClipBoard", mode=cb.Clipboard)
# Text is now already in the clipboard, no need for further actions.
sys.exit()
if __name__ == "__main__":
main()
answered Nov 9, 2015 at 11:00
AkshayAkshay
4556 silver badges15 bronze badges
2
GTK3:
#!/usr/bin/python3
from gi.repository import Gtk, Gdk
class Hello(Gtk.Window):
def __init__(self):
super(Hello, self).__init__()
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text("hello world", -1)
Gtk.main_quit()
def main():
Hello()
Gtk.main()
if __name__ == "__main__":
main()
answered Feb 25, 2015 at 15:29
Martin ThomaMartin Thoma
118k153 gold badges590 silver badges913 bronze badges
1
I try this clipboard 0.0.4 and it works well.
https://pypi.python.org/pypi/clipboard/0.0.4
import clipboard
clipboard.copy("abc") # now the clipboard content will be string "abc"
text = clipboard.paste() # text will have the content of clipboard
answered Jun 28, 2016 at 6:34
Du PengDu Peng
3432 silver badges3 bronze badges
2
One more answer to improve on:
https://stackoverflow.com/a/4203897/2804197
and https://stackoverflow.com/a/25476462/1338797 (Tkinter).
Tkinter is nice, because it’s either included with Python (Windows) or easy to install (Linux), and thus requires little dependencies for the end user.
Here I have a «full-blown» example, which copies the arguments or the standard input, to clipboard, and — when not on Windows — waits for the user to close the application:
import sys
try:
from Tkinter import Tk
except ImportError:
# welcome to Python3
from tkinter import Tk
raw_input = input
r = Tk()
r.withdraw()
r.clipboard_clear()
if len(sys.argv) < 2:
data = sys.stdin.read()
else:
data = ' '.join(sys.argv[1:])
r.clipboard_append(data)
if sys.platform != 'win32':
if len(sys.argv) > 1:
raw_input('Data was copied into clipboard. Paste and press ENTER to exit...')
else:
# stdin already read; use GUI to exit
print('Data was copied into clipboard. Paste, then close popup to exit...')
r.deiconify()
r.mainloop()
else:
r.destroy()
This showcases:
- importing Tk across Py2 and Py3
raw_input
andprint()
compatibility- «unhiding» Tk root window when needed
- waiting for exit on Linux in two different ways.
answered Nov 4, 2015 at 13:35
Tomasz GandorTomasz Gandor
7,9152 gold badges61 silver badges55 bronze badges
3
This is an altered version of @Martin Thoma’s answer for GTK3. I found that the original solution resulted in the process never ending and my terminal hung when I called the script. Changing the script to the following resolved the issue for me.
#!/usr/bin/python3
from gi.repository import Gtk, Gdk
import sys
from time import sleep
class Hello(Gtk.Window):
def __init__(self):
super(Hello, self).__init__()
clipboardText = sys.argv[1]
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text(clipboardText, -1)
clipboard.store()
def main():
Hello()
if __name__ == "__main__":
main()
You will probably want to change what clipboardText gets assigned to, in this script it is assigned to the parameter that the script is called with.
On a fresh ubuntu 16.04 installation, I found that I had to install the python-gobject
package for it to work without a module import error.
Neuron
4,8345 gold badges35 silver badges54 bronze badges
answered Apr 25, 2016 at 11:34
ProgramsterProgramster
12k8 gold badges47 silver badges54 bronze badges
I just need a python script that copies text to the clipboard.
After the script gets executed i need the output of the text to be pasted to another source.
Is it possible to write a python script that does this job?
asked Jun 16, 2012 at 12:32
3
See Pyperclip. Example (taken from Pyperclip site):
import pyperclip
pyperclip.copy('The text to be copied to the clipboard.')
spam = pyperclip.paste()
Also, see Xerox. But it appears to have more dependencies.
vauhochzett
2,2442 gold badges17 silver badges36 bronze badges
answered Jun 16, 2012 at 12:35
robertrobert
32.4k8 gold badges52 silver badges72 bronze badges
4
On macOS, use subprocess.run
to pipe your text to pbcopy
:
import subprocess
data = "hello world"
subprocess.run("pbcopy", text=True, input=data)
It will copy «hello world» to the clipboard.
answered Jun 28, 2013 at 18:27
kyle kkyle k
4,9048 gold badges30 silver badges45 bronze badges
5
To use native Python directories, use:
import subprocess
def copy2clip(txt):
cmd='echo '+txt.strip()+'|clip'
return subprocess.check_call(cmd, shell=True)
on Mac, instead:
import subprocess
def copy2clip(txt):
cmd='echo '+txt.strip()+'|pbcopy'
return subprocess.check_call(cmd, shell=True)
Then use:
copy2clip('This is on my clipboard!')
to call the function.
answered Dec 8, 2016 at 0:47
BinyaminBinyamin
6098 silver badges17 bronze badges
8
PyQt5:
from PyQt5.QtWidgets import QApplication
import sys
def main():
app = QApplication(sys.argv)
cb = QApplication.clipboard()
cb.clear(mode=cb.Clipboard )
cb.setText("Copy to ClipBoard", mode=cb.Clipboard)
# Text is now already in the clipboard, no need for further actions.
sys.exit()
if __name__ == "__main__":
main()
answered Nov 9, 2015 at 11:00
AkshayAkshay
4556 silver badges15 bronze badges
2
GTK3:
#!/usr/bin/python3
from gi.repository import Gtk, Gdk
class Hello(Gtk.Window):
def __init__(self):
super(Hello, self).__init__()
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text("hello world", -1)
Gtk.main_quit()
def main():
Hello()
Gtk.main()
if __name__ == "__main__":
main()
answered Feb 25, 2015 at 15:29
Martin ThomaMartin Thoma
118k153 gold badges590 silver badges913 bronze badges
1
I try this clipboard 0.0.4 and it works well.
https://pypi.python.org/pypi/clipboard/0.0.4
import clipboard
clipboard.copy("abc") # now the clipboard content will be string "abc"
text = clipboard.paste() # text will have the content of clipboard
answered Jun 28, 2016 at 6:34
Du PengDu Peng
3432 silver badges3 bronze badges
2
One more answer to improve on:
https://stackoverflow.com/a/4203897/2804197
and https://stackoverflow.com/a/25476462/1338797 (Tkinter).
Tkinter is nice, because it’s either included with Python (Windows) or easy to install (Linux), and thus requires little dependencies for the end user.
Here I have a «full-blown» example, which copies the arguments or the standard input, to clipboard, and — when not on Windows — waits for the user to close the application:
import sys
try:
from Tkinter import Tk
except ImportError:
# welcome to Python3
from tkinter import Tk
raw_input = input
r = Tk()
r.withdraw()
r.clipboard_clear()
if len(sys.argv) < 2:
data = sys.stdin.read()
else:
data = ' '.join(sys.argv[1:])
r.clipboard_append(data)
if sys.platform != 'win32':
if len(sys.argv) > 1:
raw_input('Data was copied into clipboard. Paste and press ENTER to exit...')
else:
# stdin already read; use GUI to exit
print('Data was copied into clipboard. Paste, then close popup to exit...')
r.deiconify()
r.mainloop()
else:
r.destroy()
This showcases:
- importing Tk across Py2 and Py3
raw_input
andprint()
compatibility- «unhiding» Tk root window when needed
- waiting for exit on Linux in two different ways.
answered Nov 4, 2015 at 13:35
Tomasz GandorTomasz Gandor
7,9152 gold badges61 silver badges55 bronze badges
3
This is an altered version of @Martin Thoma’s answer for GTK3. I found that the original solution resulted in the process never ending and my terminal hung when I called the script. Changing the script to the following resolved the issue for me.
#!/usr/bin/python3
from gi.repository import Gtk, Gdk
import sys
from time import sleep
class Hello(Gtk.Window):
def __init__(self):
super(Hello, self).__init__()
clipboardText = sys.argv[1]
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text(clipboardText, -1)
clipboard.store()
def main():
Hello()
if __name__ == "__main__":
main()
You will probably want to change what clipboardText gets assigned to, in this script it is assigned to the parameter that the script is called with.
On a fresh ubuntu 16.04 installation, I found that I had to install the python-gobject
package for it to work without a module import error.
Neuron
4,8345 gold badges35 silver badges54 bronze badges
answered Apr 25, 2016 at 11:34
ProgramsterProgramster
12k8 gold badges47 silver badges54 bronze badges
7 / 6 / 7 Регистрация: 10.08.2015 Сообщений: 63 |
|
1 |
|
27.08.2015, 11:01. Показов 62923. Ответов 10
Возникла необходимость написать программу работающую с буфером обмена Windows. Поиск в гугле по схожим запросам практически не дал результатов. Только о Clipboard в Tkinter, но без подробных описаний или примеров. P.S. Желательны ресурсы на русском языке.
__________________
0 |
ВАСИЛЕВС 561 / 484 / 168 Регистрация: 14.02.2012 Сообщений: 1,561 |
||||
27.08.2015, 11:18 |
2 |
|||
Я нашел модуль pyperclip
2 |
7 / 6 / 7 Регистрация: 10.08.2015 Сообщений: 63 |
|
27.08.2015, 11:45 [ТС] |
3 |
Подобное я тоже находил. Но paste/copy маловато для моей программы. К примеру я выделяю в браузере «100» нажимаю ctrL+c. Значение 100 записывается в переменную x. Происходит действие x+50. И при нажатии ctrl+v возвращал «150»
0 |
4606 / 2027 / 359 Регистрация: 17.03.2012 Сообщений: 10,081 Записей в блоге: 6 |
|
27.08.2015, 11:46 |
4 |
В PyQT есть QClipboard.
0 |
akzo 71 / 67 / 6 Регистрация: 08.08.2013 Сообщений: 286 Записей в блоге: 8 |
||||
27.08.2015, 11:59 |
5 |
|||
Решение
Это простая программа в процедурном стиле, к этому добавляете еще знания отсюда: http://ehow.kurortmag.net/page… aad37.html
1 |
1483 / 457 / 52 Регистрация: 10.04.2009 Сообщений: 7,874 |
|
23.02.2021, 13:36 |
6 |
здравствуйте гугл по запросу
0 |
Garry Galler 5403 / 3827 / 1214 Регистрация: 28.10.2013 Сообщений: 9,554 Записей в блоге: 1 |
||||
23.02.2021, 14:25 |
7 |
|||
есть пример попроще? Ты как всегда решил сначала спросить и только потом попробовать?
1 |
Ципихович Эндрю 1483 / 457 / 52 Регистрация: 10.04.2009 Сообщений: 7,874 |
||||||||
23.02.2021, 14:41 |
8 |
|||||||
нет нужно по аналогии
и после того как скопировали и нажали ОК в переменную
0 |
Garry Galler 5403 / 3827 / 1214 Регистрация: 28.10.2013 Сообщений: 9,554 Записей в блоге: 1 |
||||
23.02.2021, 16:59 |
9 |
|||
нет нужно по аналогии Не понимаю твоей аналогии. Добавлено через 51 минуту
А всякого рода нажатия OK или Enter никакого отношения к работе буфера не имеют.
2 |
1483 / 457 / 52 Регистрация: 10.04.2009 Сообщений: 7,874 |
|
23.02.2021, 17:10 |
10 |
А всякого рода нажатия OK или Enter никакого отношения к работе буфера не имеют. я это уже вспомнил, есть инструменты для уточнения последнегопредпоследнего и других по счёту элементов — что в буфере находятся? Добавлено через 1 минуту
ровно две функции: copy (скопировать что-то), paste (вставить из буфера в другое место) ну так и скопировать что-то и залетать в значение переменной
0 |
5403 / 3827 / 1214 Регистрация: 28.10.2013 Сообщений: 9,554 Записей в блоге: 1 |
|
23.02.2021, 17:22 |
11 |
что в буфере находятся В буфере находится ровно один элемент (не считая того, что он может быть одновременно в нескольких форматах). Нет никаких первых-последних.
1 |
Pyperclip – это небольшой кроссплатформенный (Windows, Linux, OS X) модуль для копирования и вставки текста в буфер обмена, разработанный Элом Свейгартом. Он работает на Python 2 и 3 и устанавливается с помощью pip:
pip install pyperclip
Содержание
- 1 Функционал
- 2 Пример
Функционал
В системах Microsoft Windows дополнительный пакет не требуется, поскольку он взаимодействует непосредственно с Windows API через модуль ctypes.
В дистрибутивах Linux модуль использует одну из следующих программ: xclip и xsel. Если какие-либо из них не установлены по умолчанию, их можно получить, выполнив команду:
Code language: JavaScript (javascript)
sudo apt-get install xclip sudo apt-get install xsel
Если таковых нет, pyperclip может использовать функции Qt (PyQt 4) или GTK (недоступно в Python 3), если они установлены.
В Mac OS X для этого используются pbcopy и pbpaste.
Пример
Code language: PHP (php)
>>> import pyperclip as clipboard # Копирование текста в буфер обмена. >>> clipboard.copy("Текст для копирования") # Получить доступ к содержимому (вставить). >>> clipboard.paste() 'Текст для копирования'
Модуль clipboard.Работа с буфером обмена
Знатокам в питоне будет неинтересно. Возможно будет интересно глянуть начинающим программистам.Бывало раньше интересовал подобный вопрос. Вот решил накатать такую статейку. Пусть будет до кучи Автор статьи я(zaterehniy). Работа довольно таки простая, всего две команды одна команда записывает информацию в буфер, другая записанную информацию достает из него. Конечно же чтобы наше приложение на питоне могло работать с буфером нужно импортировать сам модуль.
import clipboard
Для записи данных в буфер служит следуюший метод —
clipboard.Set() (здесь стоит обратить внимание что Set писать следует именно с большой буквы! Если напишем с маленькой- set, то консоль выдаст ошибку),в скобках пишем любой текст,который мы поместим в буфер,не забываем про ковычки которые указывают питону что внутри них просто текст.
clipboard.Set(‘luboi tekst’)
Для того чтобы достать данные из буфера пишем следующую команду(достать просто командой get не получится).
clipboard.Get()
a = clipboard.Get()
print ‘ module clipboard= ‘a
module clipboard = luboi tekst
Источник новости:
I was looking for ways to listen to the clipboard and get called for updates as its content changes.
I found a couple of ways to achieve this in Python. Some solutions poll for changes, others use ctypes and Win32 APIs.
Working with C bindings in Python is frustrating. The debugger doesn’t work well with pointers and C types. You have to build your own C structs to unpack a pointer. Win32 APIs are no exception, as they’re written in C/C++ and Python is probably not the best language to use them. But still, it works well enough for our purposes.
We’ll explore how do to it in Python using Win32 APIs, or alternatively integrating a utility I’ve written in C# that retrieves clipboard contents or streams updates to it. I had written this earlier when I didn’t know much about Win32 APIs or how to use them, but it still functions well, so I’m leaving it here in this post as reference.
To make working with Win32 APIs easier, we need to install pywin32
package which provides most of the primitives and types for Win32 APIs, though it’s not a strict dependency.
Monitoring clipboard updates
Windows provides a couple of methods for data exchange between applications. Clipboard is one of them. All applications have access to it. But we first need to create a primitive «application» that Windows recognizes. We subscribe it for the clipboard updates.
Windows uses windows (hah!) as the building block of applications. I’ve written about how windows and messaging works on Windows in another post where I explored USB hotplugging events, which might be worth reading.
Let’s create a window, and set print
function as its window procedure:
import win32api, win32gui
def create_window() -> int:
"""
Create a window for listening to messages
:return: window hwnd
"""
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = print
wc.lpszClassName = 'demo'
wc.hInstance = win32api.GetModuleHandle(None)
class_atom = win32gui.RegisterClass(wc)
return win32gui.CreateWindow(class_atom, 'demo', 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None)
if __name__ == '__main__':
hwnd = create_window()
win32gui.PumpMessages()
When we run it doesn’t do much except to dump messages sent by Windows to console. We receive the first message WM_DWMNCRENDERINGCHANGED
, which doesn’t concern us.
We need to register this window as a «clipboard format listener» using AddClipboardFormatListener
API, to get notified by Windows whenever the contents of the clipboard change.
import ctypes
# ...
if __name__ == '__main__':
hwnd = create_window()
ctypes.windll.user32.AddClipboardFormatListener(hwnd)
win32gui.PumpMessages()
Now when we run this, it still prints the previous message, but when you copy something to the clipboard it receives another message:
2033456 799 1 0
2033456 797 8 0
Decoding the second message:
Value | Hex | Message |
---|---|---|
797 |
0x031D |
WM_CLIPBOARDUPDATE 🥳 |
We’ve received a WM_CLIPBOARDUPDATE
message notifying us that the clipboard content has changed. Now we can build our script around it.
import threading
import ctypes
import win32api, win32gui
class Clipboard:
def _create_window(self) -> int:
"""
Create a window for listening to messages
:return: window hwnd
"""
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = self._process_message
wc.lpszClassName = self.__class__.__name__
wc.hInstance = win32api.GetModuleHandle(None)
class_atom = win32gui.RegisterClass(wc)
return win32gui.CreateWindow(class_atom, self.__class__.__name__, 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None)
def _process_message(self, hwnd: int, msg: int, wparam: int, lparam: int):
WM_CLIPBOARDUPDATE = 0x031D
if msg == WM_CLIPBOARDUPDATE:
print('clipboard updated!')
return 0
def listen(self):
def runner():
hwnd = self._create_window()
ctypes.windll.user32.AddClipboardFormatListener(hwnd)
win32gui.PumpMessages()
th = threading.Thread(target=runner, daemon=True)
th.start()
while th.is_alive():
th.join(0.25)
if __name__ == '__main__':
clipboard = Clipboard()
clipboard.listen()
One thing we need to watch out for is that because win32gui.PumpMessages()
is blocking, we cannot stop the script using Ctrl + C. So we run it inside a thread, which lets KeyboardInterrupt
to bubble up and terminate the script.
When we run it, and copy something (text, files) and check the console, we can see it prints clipboard updated!
.
Now that we have the notification working, let’s retrieve the what’s actually in the clipboard.
Getting clipboard contents
Windows clipboard has a concept called «clipboard format». When you copy something, (depending on application) the payload is also attached a bunch of metadata, allowing it to be used in various contexts. For example, when you copy a table from a webpage, you have the option to paste it as plain text, or paste it in Excel and have it formatted as a table. You can copy files, images, screenshots into the clipboard and each payload gets stored formatted (again, depending on how the application sets the clipboard content).
Therefore, if we want to get the clipboard contents, we need to specify which format we want in. For now, we’ll be dealing with:
Format | Value | Description |
---|---|---|
CF_UNICODETEXT |
13 |
Unicode text format |
CF_TEXT |
1 |
Text format for ANSI text |
CF_HDROP |
15 |
List of files |
CF_BITMAP |
2 |
Images e.g. screenshots |
To read the clipboard, we’ll use OpenClipboard
to set a lock first. This ensures other programs can’t modify the clipboard while we’re trying to read it. We need to release the lock with CloseClipboard
once we’re done.
Then we’ll call IsClipboardFormatAvailable
to query a format, then get its contents using GetClipboardData
, or fallback to other formats.
from pathlib import Path
from dataclasses import dataclass
from typing import Union, List, Optional
import win32clipboard, win32con
@dataclass
class Clip:
type: str
value: Union[str, List[Path]]
def read_clipboard() -> Optional[Clip]:
try:
win32clipboard.OpenClipboard()
if win32clipboard.IsClipboardFormatAvailable(win32con.CF_HDROP):
data: tuple = win32clipboard.GetClipboardData(win32con.CF_HDROP)
return Clip('files', [Path(f) for f in data])
elif win32clipboard.IsClipboardFormatAvailable(win32con.CF_UNICODETEXT):
data: str = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
return Clip('text', data)
elif win32clipboard.IsClipboardFormatAvailable(win32con.CF_TEXT):
data: bytes = win32clipboard.GetClipboardData(win32con.CF_TEXT)
return Clip('text', data.decode())
elif win32clipboard.IsClipboardFormatAvailable(win32con.CF_BITMAP):
# TODO: handle screenshots
pass
return None
finally:
win32clipboard.CloseClipboard()
if __name__ == '__main__':
print(read_clipboard())
When we run it, and try copying some text or files, it prints the contents to the console:
Clip(type='text', value='read_clipboard')
Clip(type='files', value=[WindowsPath('C:/Python39/vcruntime140_1.dll'), WindowsPath('C:/Python39/python.exe')])
Now let’s bring it all together:
Clipboard listener in Python
I’ve placed read_clipboard
inside Clipboard
class, which creates a window and subscribes to clipboard updates. When the clipboard content changes, it triggers suitable callbacks with the parsed content.
For convenience, you can enable trigger_at_start
to trigger callbacks with the current clipboard content immediately after listening.
import ctypes
import threading
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, Union, List, Optional
import win32api, win32clipboard, win32con, win32gui
class Clipboard:
@dataclass
class Clip:
type: str
value: Union[str, List[Path]]
def __init__(
self,
trigger_at_start: bool = False,
on_text: Callable[[str], None] = None,
on_update: Callable[[Clip], None] = None,
on_files: Callable[[str], None] = None,
):
self._trigger_at_start = trigger_at_start
self._on_update = on_update
self._on_files = on_files
self._on_text = on_text
def _create_window(self) -> int:
"""
Create a window for listening to messages
:return: window hwnd
"""
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = self._process_message
wc.lpszClassName = self.__class__.__name__
wc.hInstance = win32api.GetModuleHandle(None)
class_atom = win32gui.RegisterClass(wc)
return win32gui.CreateWindow(class_atom, self.__class__.__name__, 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None)
def _process_message(self, hwnd: int, msg: int, wparam: int, lparam: int):
WM_CLIPBOARDUPDATE = 0x031D
if msg == WM_CLIPBOARDUPDATE:
self._process_clip()
return 0
def _process_clip(self):
clip = self.read_clipboard()
if not clip:
return
if self._on_update:
self._on_update(clip)
if clip.type == 'text' and self._on_text:
self._on_text(clip.value)
elif clip.type == 'files' and self._on_text:
self._on_files(clip.value)
@staticmethod
def read_clipboard() -> Optional[Clip]:
try:
win32clipboard.OpenClipboard()
def get_formatted(fmt):
if win32clipboard.IsClipboardFormatAvailable(fmt):
return win32clipboard.GetClipboardData(fmt)
return None
if files := get_formatted(win32con.CF_HDROP):
return Clipboard.Clip('files', [Path(f) for f in files])
elif text := get_formatted(win32con.CF_UNICODETEXT):
return Clipboard.Clip('text', text)
elif text_bytes := get_formatted(win32con.CF_TEXT):
return Clipboard.Clip('text', text_bytes.decode())
elif bitmap_handle := get_formatted(win32con.CF_BITMAP):
# TODO: handle screenshots
pass
return None
finally:
win32clipboard.CloseClipboard()
def listen(self):
if self._trigger_at_start:
self._process_clip()
def runner():
hwnd = self._create_window()
ctypes.windll.user32.AddClipboardFormatListener(hwnd)
win32gui.PumpMessages()
th = threading.Thread(target=runner, daemon=True)
th.start()
while th.is_alive():
th.join(0.25)
if __name__ == '__main__':
clipboard = Clipboard(on_update=print, trigger_at_start=True)
clipboard.listen()
When we run it and copy some text, or some files, it dumps the clipboard content just as we want it.
Clipboard.Clip(type='text', value='Clipboard')
Clipboard.Clip(type='files', value=[WindowsPath('C:/Python39/python.exe')])
I haven’t managed to retrieve bitmap from the clipboard when taking a screenshot yet, though it shouldn’t be too difficult.
It should prove useful for the use case where when you take a screenshot, you can save it automatically as PNG, upload it and copy its URL to clipboard, ready for pasting.
Using dumpclip: polling for changes
Before I could navigate around Win32 APIs easily, I used higher level APIs provided in C# to listen to the clipboard. On that end, I created a mini utility called dumpclip that prints the clipboard content to console as JSON or streams clipboard updates.
The first version of dumpclip had a single function: dumping the clipboard content to console as JSON.
> dumpclip.v1.exe
{"text":"monitor"}
Calling it from Python is quite straightforward using subprocess
module. But that also meant polling for changes every second.
import json
import subprocess
import threading
from time import sleep
from typing import Callable
def get_clipboard() -> dict:
proc = subprocess.run(
["dumpclip.v1.exe"],
stdout=subprocess.PIPE,
text=True,
)
if proc.returncode != 0:
return {}
return json.loads(proc.stdout)
def monitor_clipboard(on_change: Callable[[dict], None]) -> None:
def monitor():
old = None
while True:
new = get_clipboard()
if old != new:
on_change(new)
old = new
sleep(1)
th = threading.Thread(target=monitor)
th.start()
th.join()
if __name__ == "__main__":
monitor_clipboard(on_change=print)
It’s functional, but we can do better.
Using dumpclip: streaming clipboard updates
The second iteration of dumpclip involved using Win32 APIs. I’ve used AddClipboardFormatListener
to register a callback for clipboard changes in C#, then retrieved & dumped its content as the new content came in.
> dumpclip.v2.exe --listen
{"text":"ClipboardChanged"}
{"text":"monitor"}
{"files":["D:\path\to\file.ext"]}
...
This worked much better. I can process its stdout
stream, and trigger a callback directly, instead of polling for changes. But dumpclip launched in listener mode never terminates. We need to read its stdout in real-time.
To stream stdout
of a process, we need to launch it with subprocess.Popen
and pipe its output to subprocess.PIPE
.
Then we can read its stdout
in a separate thread. Because, the main thread that launches the process will be waiting for the process to terminate (although it never will).
import json
import subprocess
import threading
from typing import Callable
def monitor_clipboard(on_change: Callable[[dict], None]) -> None:
def read_stdout(proc: subprocess.Popen):
for line in iter(proc.stdout.readline, ""):
if line.strip():
payload = json.loads(line)
on_change(payload)
proc = subprocess.Popen(
["dumpclip.v2.exe", "--listen"],
text=True,
stdout=subprocess.PIPE,
)
th = threading.Thread(
target=read_stdout,
args=(proc,),
)
th.start()
try:
proc.wait()
except KeyboardInterrupt:
proc.kill()
raise
if __name__ == "__main__":
monitor_clipboard(on_change=print)
Because the process doesn’t terminate, the thread that consumes its output doesn’t stop, either.
It keeps processing the output as new content comes in, and idles if there’s nothing to consume, as proc.stdout.readline()
call is blocking.
When the process gets killed, proc.stdout
stops blocking and the thread terminates.
To prevent blocking interrupt signal and to allow the script and the process terminate, we need to .wait()
the subprocess. This allows KeyboardInterrupt
to bubble up and terminate the script (and its subprocesses) when we hit Ctrl + C.
Using dumpclip: async workflow
Just for kicks, I wanted to implement the same operation in async. It turned out to be more straightforward to write and consume. One caveat is that you have to create a wrapper async function to use async
/await
keywords, so I had to add a main
function to do that.
import asyncio
import json
from pathlib import Path
from typing import AsyncIterable
async def monitor_clipboard() -> AsyncIterable[dict]:
proc = await asyncio.subprocess.create_subprocess_exec(
"dumpclip.exe",
"--listen",
cwd=str(Path(__file__).parent.resolve()),
stdout=asyncio.subprocess.PIPE,
)
while True:
if raw_bytes := await proc.stdout.readline():
text = raw_bytes.decode().strip()
if text:
yield json.loads(text)
if __name__ == "__main__":
async def main():
async for clip in monitor_clipboard():
print(clip)
asyncio.get_event_loop().run_until_complete(main())
That’s it.
Cheers ✌
If you’ve found this post useful, consider sharing it.
Как читать текст из буфера обмена (windows) из python?
1625
10
10 ответов:
вы можете использовать модуль под названием win32clipboard, которая является частью pywin32.
вот пример, который сначала устанавливает данные буфера обмена, а затем получает его:
import win32clipboard # set clipboard data win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardText('testing 123') win32clipboard.CloseClipboard() # get clipboard data win32clipboard.OpenClipboard() data = win32clipboard.GetClipboardData() win32clipboard.CloseClipboard() print data
важное напоминание из документации:
когда окно закончит изучение или изменение буфера обмена,
закройте буфер обмена, вызвав CloseClipboard. Это позволяет другим
окна доступа к буферу обмена. Не устанавливайте объект на этот
буфер обмена после вызова CloseClipboard.
вы можете легко сделать это через встроенный модуль Tkinter который в основном является библиотекой GUI. Этот код создает пустой виджет для получения содержимого буфера обмена из ОС.
#from tkinter import Tk # Python 3 from Tkinter import Tk Tk().clipboard_get()
самый популярный ответ выше странный в том смысле, что он просто очищает буфер обмена, а затем получает содержимое (которое затем пусто). Можно очистить буфер обмена, чтобы убедиться, что какой-то тип содержимого буфера обмена, например «форматированный текст», не «покрывает» ваш обычный текстовый контент, который вы хотите сохранить в буфере обмена.
следующий фрагмент кода заменяет все символы новой строки в буфер обмена пробел, а затем удаляет все двойные пробелы и, наконец, сохраняет содержимое обратно буфер обмена:
import win32clipboard win32clipboard.OpenClipboard() c = win32clipboard.GetClipboardData() win32clipboard.EmptyClipboard() c = c.replace('n', ' ') c = c.replace('r', ' ') while c.find(' ') != -1: c = c.replace(' ', ' ') win32clipboard.SetClipboardText(c) win32clipboard.CloseClipboard()
Если вы не хотите устанавливать дополнительные пакеты,
ctypes
может сделать эту работу также.import ctypes CF_TEXT = 1 kernel32 = ctypes.windll.kernel32 user32 = ctypes.windll.user32 user32.OpenClipboard(0) if user32.IsClipboardFormatAvailable(CF_TEXT): data = user32.GetClipboardData(CF_TEXT) data_locked = kernel32.GlobalLock(data) text = ctypes.c_char_p(data_locked) print(text.value) kernel32.GlobalUnlock(data_locked) else: print('no text in clipboard') user32.CloseClipboard()
для меня консольная программа ответы с tkinter выше, не совсем Работа для меня, потому что .destroy() всегда давал ошибку,:
не удается вызвать команду «событие»: приложение было уничтожено во время выполнения…
или при использовании .withdraw () окно консоли не вернуло фокус обратно.
чтобы решить эту проблему, вы также должны позвонить .обновление () перед началом .уничтожать.)( Пример:
# Python 3 import tkinter r = tkinter.Tk() text = r.clipboard_get() r.withdraw() r.update() r.destroy()
Р.снять() предотвращает отображение кадра в течение миллисекунды, а затем он будет уничтожен, возвращая фокус на консоль.
использовать библиотеку питонов буфер обмена
его просто использовали так:
import clipboard clipboard.copy("this text is now in the clipboard") print clipboard.paste()
я узнал, что это был самый простой способ получить доступ к буферу обмена из Python:
1) Установите pyperclip:
pip install pyperclip
2) Использование:
import pyperclip s = pyperclip.paste() pyperclip.copy(s) # the type of s is string
протестировано на 64-битном Win10, Python 3.5. Кажется, работает и с не-ASCII символами.
Проверенные символы включают ±°©©αβγδδψφåäö
стандартная библиотека Python делает это…
try: # Python2 import Tkinter as tk except ImportError: # Python3 import tkinter as tk def getClipboardText(): root = tk.Tk() # keep the window from showing root.withdraw() return root.clipboard_get()