RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Build a Shared Clipboard Utility in Python : Page 3

A shared clipboard lets you copy and paste data seamlessly across machines—it's the perfect productivity tool if you work with multiple machines in parallel.

Windows Shared Clipboard
The Windows shared clipboard module (WindowsSharedClipboard.py, shown in Listing 2), is implemented on top of the win32clipboard module of the Win32 extensions for Python. There is a one-to-one relationship between the abstract clipboard interface and the win32clipboard interface, so each method implementation requires only a line or two; win32clipboard does the heavy lifting.

First, the code imports the win32clipboard module. The openClipboard() and closeClipboard() just call the corresponding win32clipboard methods. The closeClipboard() method also swallows any exception after printing it to the console. There is nothing it can do to recover if the error is a permanent condition. It handles the exception because in the main loop the call to closeClipboard() is not wrapped with a try-except block (it's in the outer try-finally block).

   import win32clipboard
   def openClipboard():
   def closeClipboard():
     except Exception, e:
        print e
The getClipboardData() function checks if there is text content in the clipboard and, if so, returns it (or returns None if there isn't). Any exceptions propagate up, and get caught by the try-except block in the main loop.

   def getClipboardData():
     if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_TEXT):
       return win32clipboard.GetClipboardData()
       return None
The setClipboardData() function is even simpler. It just empties the clipboard and puts the new content into it (as text). Again, exceptions propagate upward by design.

   def setClipboardData(data):
     win32clipboard.SetClipboardData(win32clipboard.CF_TEXT, data)
Author's Note: the win32clipboard module is not part of the standard Python distribution on Windows. You can get it (along with lots of other comprehensive Python bindings to Win32 APIs) as part of the Win32 extensions project on SourceForge. Alternatively, the module is also bundled with the ActivePython distribution for Windows.

Implementation for Mac OS X Leopard (Carbon)
There are actually two separate modules for the Mac shared clipboard because the Carbon module kept crashing on Mac OS Tiger. The pbcopy/pbpaste-based module should work on any Mac OS X version. On Leopard, the Carbon-based module works fine.

The CarbonSharedClipboard.py module (see Listing 3) implements the clipboard interface on top of the Mac's native Carbon API, providing clipboard (or rather "pasteboard" as it is called on the Mac) access.

The module begins with a couple of imports statements from Carbon.Scrap, which corresponds more or less to the clipboard on Windows. It also imports the MacOS to be able to access the MacOs.Error exception type. Note that openClipboard() and closeClipboard() do nothing here.

   from Carbon.Scrap import (GetCurrentScrap,
   import MacOS
   def openClipboard():
     pass # no-op on the mac
   def closeClipboard():
     pass # no-op on the mac
This implementation of the getClipboardData() function has a try-except block to catch exceptions. It gets the current scrap and tries to return any data associated with the 'TEXT' flavor, which is simply the text content of the Mac pasteboard. If it fails, the module raises an exception. If no text content exists in the clipboard the getScrapFlavorData call raises a -102 MacOS.Error. Because that's common, in this case the code just returns an empty string. However, for any other error, it re-raises the exception, which then propagates to the main loop for handling.

   def getClipboardData():
         scrap = GetCurrentScrap()
         return scrap.GetScrapFlavorData(flavorType='TEXT')
       except MacOS.Error, e:
         if e[0] != -102:
               # -102 == noTypeErr
         return ''
The setClipboardData() function is much more straightforward: clear the current scrap, get it, put the text into the pasteboard, and you're done.

   def setClipboardData(data):
     scrap = GetCurrentScrap()
     scrap.PutScrapFlavor(flavorType='TEXT', 0, text)
Implementation for All Mac OS X Versions
The other Mac module (MacSharedClipboard.py, in Listing 4) implements the clipboard interface on top of two command-line programs called pbcopy (which copies text into the clipboard) and pbpaste (which pastes whatever text is in the clipboard). The prefix "pb" stands for "pasteboard," the Mac term for clipboard.

This module begins by importing the subprocess module—a standard Python library module (new in Python 2.4) used to launch and interact with external processes. It is very handy for a variety of tasks. Again, the openClipboard() and closeClipboard() functions are empty:

   import subprocess
   def openClipboard():
     pass # no-op on the mac
   def closeClipboard():
     pass # no-op on the mac
The getClipboardData() function launches the pbpaste command line tool and intercepts its standard output. It waits for the tool to finish running, reads the data and returns it. You know the drill about exceptions by now.

   def getClipboardData():
     p = subprocess.Popen(['pbpaste'], stdout=subprocess.PIPE)
     retcode = p.wait()
     data = p.stdout.read()
     return data
In this module, the setClipboardData() function is essentially a mirror image of getClipboardData(). It launches the pbcopy tool, intercepts the standard input, writes the data to the pasteboard, closes the standard input and wait for the tool to finish running.

   def setClipboardData(data):
     p = subprocess.Popen(['pbcopy'], stdin=subprocess.PIPE)
     retcode = p.wait()
Possible Extensions to SharedClipboard
After you've implemented the basic SharedClipboard discussed in this article, you might consider extending the utility by giving it additional capabilities, such as:

  • Adding Linux Support: If you run Linux in a VM hosted on a Mac or Windows box you can simply run SharedClipboard on the host OS. However, it makes a lot of sense to extend the program to support Linux for pure Linux users. To do so will require supporting at least GNOME and KDE, the most popular Linux windows managers. You can access Gnome's clipboard using pygtk and KDE's clipboard using pyqt. Another option is to require that the target machines have xsel or xclip, which work very much like pbcopy/pbpaste and implement Linux clipboard support on top of those.
  • Adding Internet Connectivity: This is not quite as straightforward as adding a new host OS, because it will require writing a little web application that functions similarly to the shared file. It will also require some changes to SharedClipboard.py. Instead of reading and writing to a file, an Internet version would get content from the web application and post data to the web application when a user puts something new into the clipboard. Note that—depending on your connection—the latency may be noticeable when using a web-shared clipboard; but otherwise, the user experience should stay the same; copy here and paste there!
  • Adding Non-Text Support: Currently, SharedClipboard supports only shared text, and adding support for non-text types, such as images, is non-trivial. The image clipboard formats differ between different operating systems, so if you put a copied image into the shared clipboard on a Mac it is very unlikely you will be able to paste it on Windows (and vice versa). One approach would to translate the image in the clipboard from its native clipboard format to some neutral format that can be read on every machine (e.g. a PIL image) when writing to the shared file, and when reading from the shared file translating from the neutral format to some appropriate native clipboard image format before placing the file contents in the clipboard.
Whether you decide to extend it or leave it as is, SharedClipboard is a convenient little tool that can improve productivity for people working with multiple physical machines. You've toured its current design and implementation, and seen some ideas for further development. If you think of some entirely new capability for this utility, let me know.

Gigi Sayfan specializes in cross-platform object-oriented programming in C/C++/C#/Python/Java with an emphasis on large-scale distributed systems. He is currently trying to build brain-inspired intelligent machines at Numenta.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date