Qt donne accès au presse-papier via QApplication.clipboard(). Dans l'absolu, ce code fonctionnera :
>>> from PyQt5.QtWidgets import QApplication
>>> QApplication.clipboard().setText("azerty")
En pratique, cela ne fonctionne pas terrible (reproduit sur GNU/Linux et macOS, PyQt 5.11 et 5.13) :
>>> from PyQt5.QtWidgets import QApplication
>>> QApplication([]).clipboard().setText("azerty")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: wrapped C/C++ object of type QClipboard has been deleted
>>> cb = QApplication([]).clipboard()
>>> cb
<PyQt5.QtGui.QClipboard object at 0x7f514d298708>
>>> cb.setText("azerty")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: wrapped C/C++ object of type QClipboard has been deleted
>>> # PyQt 5.13+, avec un context manager
>>> from PyQt5.QtCore import QCoreApplication
>>> with QCoreApplication([]) as app:
... print(QApplication.clipboard().text())
[1] 7604 segmentation fault python
Si Qt n'est pas chaud, on peut tenter notre chance avec d'autres solutions spécifiques à chaque OS.
Nous allons créer 2 fonctions, simples d'utilisation :
# Store some text in the clipboard
copy("some text")
# Get clipboard data
paste()
GNU/Linux : xclip
Je pars sur xclip (mais xsel aurait pu faire l'affaire aussi).
from subprocess import PIPE, Popen, check_output
def copy(text: str) -> None:
"""Copy some *text* into the clipboard. The xclip tool needs to be installed.
Emulate: echo "blablabla" | xclip -selection c
"""
with Popen(["xclip", "-selection", "c"], stdin=PIPE) as p:
p.stdin.write(text.encode("utf-8"))
p.stdin.close()
p.wait()
def paste() -> str:
"""Get the text data from the clipboard. The xclip tool needs to be installed.
Emulate: xclip -selection c -o
"""
data = check_output(["xclip", "-selection", "c", "-o"])
return data.decode("utf-8")
macOS : pbcopy & pbpaste
Par chance, macOS contient déjà les outils nécessaires : pbcopy et pbpaste.
from subprocess import PIPE, Popen
def copy(text: str) -> None:
"""Copy some *text* into the clipboard.
Emulate: echo "blablabla" | pbcopy w
"""
with Popen(["pbcopy", "w"], stdin=PIPE) as p:
p.stdin.write(text.encode("utf-8"))
p.stdin.close()
p.wait()
def paste() -> str:
"""Get the text data from the clipboard.
Emulate: pbpaste r
"""
data = subprocess.check_output(["pbpaste", "r"])
return data.decode("utf-8")
⚠ À partir de macOS 10.11 (El Capitan), ces fonctions ne peuvent être utilisées lorsqu'il n'y a pas d'utilisateur connecté (ce qui est fréquent si vous pratiquez l'intégration continue). C'est une limitation de l'OS : il n'est plus possible d'avoir accès à CFPasteboardCreate quand il n'y a pas de pasteboard, càd quand la machine est bloquée à l'écran de connexion.
pbcopy[7531:6748886] CFPasteboardRef CFPasteboardCreate(CFAllocatorRef, CFStringRef) : failed to create global data
Une autre version de ces fonctions, utilisant PyObjC, souffre du même problème. Donc si une bonne âme trouve une solution, je suis preneur.
from AppKit import NSPasteboard, NSStringPboardType
from Foundation import NSString, NSUTF8StringEncoding
def copy(text: str) -> None:
pb = NSPasteboard.generalPasteboard()
pb.declareTypes_owner_([NSStringPboardType], None)
newStr = NSString.stringWithString_(text)
newData = newStr.nsstring().dataUsingEncoding_(NSUTF8StringEncoding)
pb.setData_forType_(newData, NSStringPboardType)
def paste() -> str:
pb = NSPasteboard.generalPasteboard()
return pb.stringForType_(NSStringPboardType)
Windows : win32com
Sur Windows, nous pouvons utiliser win32clipboard pour accéder aux fonctions de l'OS.
import win32clipboard
def copy(text: str) -> None:
"""Copy some *text* into the clipboard.
Emulate: CTRL + C
"""
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text, win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
def paste() -> str:
"""Get the text data from the clipboard.
Emulate: CTRL + V
"""
win32clipboard.OpenClipboard()
text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
return text