Date: Mon, 02 Nov 1998 22:59:24 -0700 From: Andrew Dalke <dalke@bioreason.com> Subject: ANN: CrossCopy Below is my first real Python program (from about 6 months ago). It mostly solved a frustration that I no longer have. The problem: Our bug database was on an NT box but our code ran on SGIs. If there was a bug in the code and we wanted to copy&paste text into the database entry form we would have to save to a file on the SGI, (re)load the file on the NT then copy&paste *that* text into the form. Needless to say, this got boring fast. One solution: At this time I was reading the Python documentation and itching to write some real code. What I wrote was a simple Tk GUI that ran an HTTP server. The GUI contained a list of servers and when a server was selected would connect to it an display the text, which could be cut&pasted (saving the manual "write to a temporary file" step). The HTTP server would listen for requests and serve the text in its text box. Another solution: Get another job where everything can be done on the same machine (the joys of a web interface to the bug database :) An implementation to the first solution is below. After some thought I decided to name is "CrossCopy." AltaVista comes up with no matching names, which I found surprising. There's all sorts of things that could be done: read configuration from a file allow users to edit the configuration add a "Clear" button pull the text directly from the cut buffer or moral equivalent recognize that you cannot connect to yourself (or use multiple threads) -- at present this causes the system to freeze. Since I no longer have the problem, I spent no more time in development. However, I figured others here might find such a program useful and even be willing to work on it (that's what ESR says, right? :) so, presented for your approval, the source. The copyright is the same as Python's, but with explicit perrmision to distribute modified versions. Andrew Dalke dalke@bioreason.com #!/usr/local/bin/python # Copyright 1998 by Andrew Dalke -- All Rights Reserved # # Permission to use, copy, modify, and distribute this software # (including modifications) and its documentation for any purpose and # without fee is hereby granted, provided that the above copyright # notice appear in all copies and that both that copyright notice and # this permission notice appear in supporting documentation. # # ANDREW DALKE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN # NO EVENT SHALL ANDREW DALKE OR BIOREASIN, INC. BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS,WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import BaseHTTPServer, urllib import sys, os, select, getopt, socket from Tkinter import * class CrossCopyHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): self.end_headers() self.wfile.write(server_text()) class RemoteInfo: def __init__(self, url, title = '', username = '', password = ''): if title == '': # human text (defaults to url) self.title = url else: self.title = title self.url = url # where is the server? self.username = username # not used self.password = password # not used def goto(self): get_text(self.url) ###################### configuration information ################ # You'll want to modify this for your system. # Should read from file ... remote = [] remote.append(RemoteInfo('http://bioreason3:8000/')) remote.append(RemoteInfo('http://bioreason8:8000/')) remote.append(RemoteInfo('http://bioreason9:8000/')) #### Get the text to pass back to the client (from the server) def server_text(): global ccserver global ccclient global root if ccclient is not None: return ccclient.text.get("0.0", "end") return root.selection_get() ####### Client GUI Code class CrossCopyClient: def __init__(self, root): # make a menu bar self.mBar = Frame(root, relief=RAISED, borderwidth=2) self.mBar.pack(fill=X) self.Command_button = self.makeCommandMenu(self.mBar) self.Edit_button = self.makeEditMenu(self.mBar) ## no remote self.mBar.tk_menuBar(self.Command_button) self.text = Text(root) self.text.pack() # menu bars def makeCommandMenu(self, mBar): Command_button = Menubutton(mBar, text='File', underline=0) Command_button.pack(side=LEFT, padx="2m") Command_button.menu = Menu(Command_button) Command_button.menu.add_command(label='Quit', underline=0, command=quit) # set up a pointer from the file menubutton back to the file menu Command_button['menu'] = Command_button.menu return Command_button def makeEditMenu(self, mBar): global remote Edit_button = Menubutton(mBar, text='Edit', underline = 0) Edit_button.pack(side=LEFT, padx="2m") Edit_button.menu = Menu(Edit_button) for r in remote: Edit_button.menu.add_command(label= r.title, command=r.goto) Edit_button['menu'] = Edit_button.menu return Edit_button ###### Server code class CrossCopyServer: def __init__(self, port): # Start the web server self.server_class = BaseHTTPServer.HTTPServer self.handler_class = CrossCopyHandler self.server_address = ('', port) self.httpd = self.server_class(self.server_address, self.handler_class) def fileno(self): return self.httpd.fileno() def handle_request(self): return self.httpd.handle_request() def get_text(url): a = urllib.urlopen(url) ccclient.text.delete('0.0', 'end') ccclient.text.insert('end', a.read()) def quit(): root.quit() def check_ccserver(): global ccserver global root if select.select([ccserver], [], [], 0) == ([ccserver], [], []): ccserver.handle_request() root.after(200, check_ccserver) if __name__ == '__main__': server = 1 client = 1 port = 8000 initfile = '/u/dalke/.crosscopyrc' # not the we use it ... yet args = sys.argv[:] try: opts, rest = getopt.getopt(sys.argv[1:], '', ['client=', 'server=', 'port=', 'file=']) if len(rest) > 2: raise getopt.error, 'too many arguments' for option, optarg in opts: if option == '--server': server = int(optarg) elif option == '--client': client = int(optarg) elif option == '--port': port = int(optarg) elif option == '--file': initfile = optarg except getopt.error, msg: print msg print 'Usage: ', print sys.argv[0], print ' [--server [0|1] ] [--client [0|1]] [--port <number>]' # should read configuration file here root = Tk() # unix specific so ignore for now (should check environ) # hostname = os.popen('hostname', 'r').read() hostname = 'CrossCopy' root.title('crosscopy on ' + hostname) if client != 0: # start the client ccclient = CrossCopyClient(root) else: ccclient = None # still need it to get the current paste selection # but don't want it to appear root.withdraw() if server != 0: # start the web server try: ccserver = CrossCopyServer(port) except socket.error, msg: print "Cannot bind to port %s: %s" % (port, msg) # whatever ... if client == 0 and server == 0: sys.exit() # must periodically check the server, if we have one if server != 0: root.after(200, check_ccserver) root.mainloop()