Welcome, guest | Sign In | My Account | Store | Cart

This is a clipboard manipulation module that demonstrates some simple use of Carbon API. It can double as a Unix-style command-line tool that prints the clipboard contents to stdout or, if specified, copies its stdin to the clipboard, although pbcopy(1) and pbpaste(1) are better suited for that.

Python, 65 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python

"""Manipulate Mac OS clipboard ("scrap") from Python.

See also: pbcopy(1), pbpaste(1)"""

from sys import stdin, stdout
from optparse import OptionParser
from Carbon.Scrap import GetCurrentScrap, ClearCurrentScrap
import MacOS

def paste(flavorType='TEXT', verbose=False):
    try:
        scrap = GetCurrentScrap()
        return scrap.GetScrapFlavorData(flavorType)
    except MacOS.Error, e:
        if verbose or e[0] != -102:
            # -102 == noTypeErr
            raise
        return ""

def copy(text, flavorType='TEXT'):
    ClearCurrentScrap()
    scrap = GetCurrentScrap()
    scrap.PutScrapFlavor(flavorType, 0, text)

def list_flavors():
    scrap = GetCurrentScrap()
    return [(name, scrap.GetScrapFlavorSize(name))
            for name, flags in scrap.GetScrapFlavorInfoList()]

def main():
    parser = OptionParser()
    parser.set_defaults(flavor='TEXT', translate=True, copy=False,
                        list_=False, verbose=False)
    parser.add_option("-c", "--copy", dest="copy", action="store_true",
                      help="copy stdin to clipboard [default: paste clipboard"
                           " to stdout]")
    parser.add_option("-l", "--list", dest="list_", action="store_true",
                      help="list currently available flavors with data sizes")
    parser.add_option("-x", "--notrans", dest="translate",
                      action="store_false",
                      help="don't translate CR to LF on output")
    parser.add_option("-f", "--flavor", dest="flavor", action="store",
                      help="specify flavor [default: TEXT]")
    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
                      help="complain if scrap flavor not found [default:"
                           " treat as empty]")
    
    options, args = parser.parse_args()
    if options.list_:
        for flavor in list_flavors():
            print "'%s' %9d" % flavor
    elif options.copy:
        copy(stdin.read(), options.flavor)
    else:
        text = paste(options.flavor, options.verbose)
        if options.translate:
            text = text.replace('\r', '\n')
        stdout.write(text)

__all__ = ['paste', 'copy', 'list_flavors', 'main']

if __name__ == "__main__":
    main()

By default, the most reasonable choice of the 'TEXT' flavour is used. Other flavors for special uses can be specified. If there is no "scrap" of a given flavor, Mac OS API report this as an error. Normally this is suppressed and treated as empty clipboard, but user can request the program to complain.

By default, the line endings are translated from '\r' (Mac) to '\n' (Unix) when printing to stdout.

3 comments

Bob Ippolito 19 years ago  # | flag

Of course, this is just about the slowest possible way to do it for ascii/rtf/postscript because Mac OS X comes with pbcopy(1) and pbpaste(1), which don't have the overhead of starting a Python interpreter, etc.

Indeed. Thanks for spoiling the fun. No, just kidding, thanks for pointing those out. Added some improvements/corrections anyway.

Simon Harrison 6 years, 9 months ago  # | flag

This will no longer work in current MacOSX versions, as born out by the documentation: https://docs.python.org/2/library/carbon.html#module-Carbon.Scrap

Artur: The only thing I got working in Sierra is to use pbpaste/pbcopy + subprocess. I was trying to implement this in ctypes because like you I don't like shelling out to external programs because there is a noticeable delay when doing so in a GUI. Not a problem for your script, because of startup time, of course.