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


Build a Shared Clipboard Utility in Python : Page 2

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.

Shared Clipboard Design
The shared clipboard design is very simple. It needs to work with different clipboard APIs on different platforms, but the main algorithm is the same. The program employs a simple generic clipboard interface consisting of the following four operations:

  • openClipboard
  • closeClipboard
  • getClipboardData
  • setClipboardData
The code consists of different modules that implement this simple interface in terms of specific APIs. The main module, SharedClipboard.py, selects the proper module at runtime based on the platform and/or configuration, and simply works in terms of the generic interface. This design has a couple of benefits. First, the main algorithm is expressed in generic terms and is not a mess of specific API calls. The design is also extensible to new platforms and APIs (as long as they can be abstracted using the interface operations).

Shared Clipboard Implementation
You can see the full code for the main module (SharedClipboard.py) in Listing 1, but here's how it works. First, it imports some standard library modules (os, sys, time). The sys.platform attribute lets Python detect which platform the program is running on. Based on the platform, it imports all the attributes from the appropriate module using the from X import * notation. That makes all the interface methods available (openClipboard, closeClipboard, getClipboardData, setClipboardData).

   import os
   import sys
   import time
   if sys.platform == 'darwin':
     if use_carbon:
       from MacSharedClipboard import *
       from CarbonSharedClipboard import *
   elif sys.platform == 'win32':
     from WindowsSharedClipboard import *
Note that if the platform is 'darwin' (Mac OS X), the module must make one further decision based the value of the use_carbon Boolean flag.

The monitorClipboard function contains the main loop. It initializes the variable prev_data to an empty string and starts a loop (I don't want to say infinite loop, because I have personally terminated this loop many times, so I know for a fact it is not infinite). Next, it opens the clipboard using by calling the openClipboard() method. The actual implantation code for openClipboard comes from one of the pre-selected platform-specific modules. If the openClipboard call fails, the code raises an exception—although the program simply catches the exception quietly and the code continues to the next loop iteration, which sleeps for a second and then attempts to open the clipboard again. That's because it's likely that some other program had the clipboard open temporarily. There is no need to panic and exit.

  def monitorClipboard(clipboard_file):
     prev_data = ''
     while (True):
         print 'OpenClipboard() failed'
If the clipboard opens successfully the code enters two nested try blocks. The external try-finally block just ensures that the program will call closeClipboard() if openClipboard() succeeded. The inner try-except block handles any exceptions raised during the main operation.

        except Exception, e:
         print e
Author's Note: The nested try blocks are necessary for backward compatibility with older version of Python. Python 2.5 introduced an improvement to the exception-handling syntax (along with many other improvements) so now you could write the following equivalent construct:

     except Exception, e:
      print e
This new syntax is much nicer because you save a potentially confusing nested block and a whole level of indentation. You can read more about Python 2.5 in my three-part series (see the Related Resources section of this article in the left column for links).

The core of the SharedClipboard program first checks whether something new has been placed in the clipboard. If so, it writes the clipboard data to the shared file, and updates the prev_data variable which keeps the last-written data so it can compare that to the clipboard contents during the next iteration. This practice prevents the program from writing the same content repeatedly if it just sits in the clipboard because the user forgot to paste it.

If there is nothing new in the clipboard, it then checks the shared file for something new—perhaps the clipboard contents from a different machine has been written to the shared file since the last check. If there is something new in the file copies that content to the local clipboard, once again updating the prev_data variable with the latest content.

      data = getClipboardData()
      if data and data != prev_data:
         open(clipboard_file, 'w').write(data)
         print 'writing %s to file' % data
         prev_data = data
        data = open(clipboard_file, 'r').read()
        if data != prev_data:
         print 'putting %s in clipboard' % data
         prev_data = data
You might think from looking at the preceding code that you could just place the line prev_data = data outside of the if-else block, because it appears to be duplicated in both the if and the else portions of the block. But you can't—there is a case when there is nothing in the clipboard (just after you paste the clipboard contents) and if you externalize this line then it will result in the shared file's contents being put into the clipboard repeatedly.

That leaves only the "main" function, which is pretty standard stuff, so you only need to read about it if you are new to Python. Python doesn't really have a main function. Any code that's not inside a function or a class definition gets executed when the hosting module is executed or imported (see the sidebar "Why Doesn't SharedClipboard Use a Clipboard Class" for more about Python and classes). However, it is common practice to have some conditional code running, such as this block:

   if __name__=='__main__'
This code executes only when the module is executed directly and not when it is imported. The SharedClipboard program follows this convention and immediately calls a main() function. Note that this convention is not required. Most Python programmers simply write their "main" code immediately following the check for __name__=='__main__'. The main() function verifies that the arguments (sys.argv) contain a filename (for the shared file) and launches monitorClipboard. It could be a little more thorough and check that the file exists, is writable, and that the SharedClipboard has sufficient permissions to access it for writing, but as I wrote it sheerly as a utility for my own private use, I kept it lightweight. If such an issue should arise, the shared clipboard will just not work—and I'll notice when I try to paste something I copied on a different machine. For debugging purposes, the following error will be printed on the SharedClipboard console window (which I keep minimized) for me to stare at.

   def main():
     usage = \
   """Usage: python SharedClipboard.py <shared clipboard filename>
   The filename should refer to a writable existing file. The file
   should be on a shared location visible and (writable) to all the
   shared clipboard instances on all machines.
     if len(sys.argv) != 2 or not os.path.isfile(sys.argv[1]):
       print usage
     clipboard_file = sys.argv[1]
   if __name__=='__main__':

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date