[Inteproxy-commits] r65 - in trunk: . inteproxy
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Thu Apr 26 21:04:26 CEST 2007
Author: bh
Date: 2007-04-26 21:04:26 +0200 (Thu, 26 Apr 2007)
New Revision: 65
Added:
trunk/inteproxy/InteProxy-icon.png
trunk/inteproxy/app.py
trunk/inteproxy/gtkapp.py
Modified:
trunk/ChangeLog
trunk/inteproxy/feesdialog.py
trunk/inteproxy/getpassword.py
trunk/inteproxy/httpserver.py
trunk/inteproxy/main.py
trunk/inteproxy/proxycore.py
Log:
* inteproxy/app.py: New. Base application class.
* inteproxy/gtkapp.py: New. GTK based application with mainwindow
and status icon
* inteproxy/main.py (the_application): New. Global variable to
hold the application object
(run_server): Instantiate the gtk InteProxyApplication and run it.
* inteproxy/getpassword.py (get_password_with_cache): Ask for the
password via the application.
* inteproxy/feesdialog.py (handle_fees_and_access_constraints):
Run the dialog via the application
* inteproxy/httpserver.py (ServerThread): New. Class to run the
proxy server in a separate thread.
* inteproxy/InteProxy-icon.png: New. Status icon.
* inteproxy/proxycore.py: Tweak import statements because of newly
introduced circular imports.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/ChangeLog 2007-04-26 19:04:26 UTC (rev 65)
@@ -1,3 +1,28 @@
+2007-04-26 Bernhard Herzog <bh at intevation.de>
+
+ * inteproxy/app.py: New. Base application class.
+
+ * inteproxy/gtkapp.py: New. GTK based application with mainwindow
+ and status icon
+
+ * inteproxy/main.py (the_application): New. Global variable to
+ hold the application object
+ (run_server): Instantiate the gtk InteProxyApplication and run it.
+
+ * inteproxy/getpassword.py (get_password_with_cache): Ask for the
+ password via the application.
+
+ * inteproxy/feesdialog.py (handle_fees_and_access_constraints):
+ Run the dialog via the application
+
+ * inteproxy/httpserver.py (ServerThread): New. Class to run the
+ proxy server in a separate thread.
+
+ * inteproxy/InteProxy-icon.png: New. Status icon.
+
+ * inteproxy/proxycore.py: Tweak import statements because of newly
+ introduced circular imports.
+
2007-04-24 Bernhard Herzog <bh at intevation.de>
* InteProxy.py, inteproxy/main.py: Move practically everything
Added: trunk/inteproxy/InteProxy-icon.png
===================================================================
(Binary files differ)
Property changes on: trunk/inteproxy/InteProxy-icon.png
___________________________________________________________________
Name: svn:mime-type
+ image/png
Added: trunk/inteproxy/app.py
===================================================================
--- trunk/inteproxy/app.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/app.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -0,0 +1,87 @@
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Base application class for the InteProxy."""
+
+from inteproxy.proxycore import MasterWorkerServer, InteProxyHTTPRequestHandler
+from inteproxy.httpserver import ServerThread
+
+import inteproxy.getpassword
+from inteproxy.feesdialog import run_fees_dialog
+
+
+class InteProxyApplication(object):
+
+ """Base Application object
+
+ An InteProxyApplication instance servers as the basis of the user
+ interface of InteProxy. This base class implements some basic
+ functionality needed by all InteProxy application objects. Derived
+ classes provide a concrete user interface with e.g. a real GUI or a
+ mock interface for tests.
+
+ Each InteProxyApplication has a server object that implements the
+ actual proxy. It can be any server object compatible with
+ inteproxy.httpserver.ServerThread, but it will usually be an
+ inteproxy.proxycore.MasterWorkerServer instance.
+ InteProxyApplication can can run this server in a separate thread.
+ """
+
+ def __init__(self, server):
+ """Inititialize the InteProxyApplication with the given server object"""
+ self.server = server
+ self.thread = None
+
+ def start_inte_proxy_thread(self, daemon):
+ """Start a new thread running the proxy
+
+ If the parameter daemon is true, the new thread will be daemon
+ thread (see Python's threading module for details on deamon
+ threads).
+ """
+ self.thread = ServerThread(self.server)
+ self.thread.start(daemon=daemon)
+
+ def stop_inte_proxy_thread(self):
+ """Stops a server thread previously started with start_inte_proxy_thread
+ """
+ self.thread.stop()
+
+ def run(self):
+ """Runs the InteProxy application.
+
+ The default implementation simply calls the server's
+ serve_forever method. Derived classes may do something more
+ sophisticated such as running the server in a separate thread
+ and a GUI in the main thread.
+ """
+ self.server.serve_forever()
+
+ def get_password(self, title):
+ """Callback to ask for a username and password.
+
+ The parameter title should be a string with a title for the
+ password dialog. The title should contain all information the
+ user may need to determine which username and password they have
+ to enter. This method returns the entered username/password
+ pair as a tuple.
+
+ This method should be called by request handlers of the server
+ when they need to query the user for usernames and passwords.
+ The default implementation simply passes all parameters through
+ to inteproxy.getpassword.getpassword.
+ """
+ return inteproxy.getpassword.getpassword(title)
+
+ def run_fees_dialog(self, title, fees, access_constraints):
+ """Callback to run the fees dialog.
+
+ The parameters have the same meaning as in the function
+ inteproxy.feesdialog.run_fees_dialog. The default
+ implementation simply calls that function.
+ """
+ run_fees_dialog(title, fees, access_constraints)
Property changes on: trunk/inteproxy/app.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
Modified: trunk/inteproxy/feesdialog.py
===================================================================
--- trunk/inteproxy/feesdialog.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/feesdialog.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -18,6 +18,9 @@
gtk = None
+import inteproxy.main
+
+
def run_fees_dialog(title, fees, access_constraints):
"""Shows fees and access_constraints information in a dialog
@@ -130,7 +133,8 @@
if is_get_capabilities_url(remote_url):
title, fees, access_constraints = parse_capabilities(response_read())
- run_fees_dialog(title, fees, access_constraints)
+ inteproxy.main.the_application.run_fees_dialog(title, fees,
+ access_constraints)
dialog_shown_for[remote_url] = True
Modified: trunk/inteproxy/getpassword.py
===================================================================
--- trunk/inteproxy/getpassword.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/getpassword.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -10,6 +10,8 @@
import threading
import traceback
+import inteproxy.main
+
# Determine the window system we're using. This determines how the user
# is asked for the username/password.
try:
@@ -107,7 +109,7 @@
user, password = pw_cache[path]
else:
title = "Username for %s: " % path
- user, password = getpassword(title)
+ user, password = inteproxy.main.the_application.get_password(title)
pw_cache[path] = user, password
return user, password
Added: trunk/inteproxy/gtkapp.py
===================================================================
--- trunk/inteproxy/gtkapp.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/gtkapp.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -0,0 +1,223 @@
+# Copyright (C) 2007 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""GTK Based InteProxyApplication"""
+
+import sys
+import os
+
+import gtk
+
+import inteproxy.app
+import inteproxy.main
+
+
+# UI definition with the menus used in the InteProxy GUI
+ui_definition = """<ui>
+ <!-- Menubar for the main window -->
+ <menubar name='Menubar'>
+ <menu action='FileMenu'>
+ <menuitem action='Close'/>
+ <menuitem action='Quit'/>
+ </menu>
+ <menu action='HelpMenu'>
+ <menuitem action='About'/>
+ </menu>
+ </menubar>
+
+ <!-- Menu for the Status Icon -->
+ <menubar name='StatusIconMenuBar'>
+ <menu action='StatusIconMenu'>
+ <menuitem action='ShowWindow'/>
+ <menuitem action='About'/>
+ <separator/>
+ <menuitem action='Quit'/>
+ </menu>
+ </menubar>
+
+</ui>"""
+
+
+
+class StatusIcon(object):
+
+ """Icon for the system tray.
+
+ The StatusIcon is a small icon in the system tray. The user can
+ interact with that icon by clicking on it to show the main window or
+ to popup a menu with the most important commands.
+ """
+
+ def __init__(self, application):
+ """Initializes the StatusIcon with the application to which it belongs
+ """
+ self.application = application
+
+ iconfile = os.path.join(os.path.dirname(__file__), "InteProxy-icon.png")
+ self.status_icon = gtk.status_icon_new_from_file(iconfile)
+ self.status_icon.set_tooltip("InteProxy")
+ self.status_icon.connect("activate", self.show_window)
+ self.status_icon.connect("popup-menu", self.popup_menu)
+
+ def popup_menu(self, status_icon, button, activate_time):
+ """Pops up the StatusIconMenu.
+
+ This method is a signal handler for the status icon's popup-menu
+ signal.
+ """
+ ui = self.application.ui
+ menu = ui.get_widget('/StatusIconMenuBar/StatusIconMenu').get_submenu()
+ menu.popup(None, None, None, button, activate_time)
+
+ def show_window(self, *args):
+ """Shows the main window"""
+ self.application.show_main_window()
+
+ def quit(self, *args):
+ """Quits the inteproxy application"""
+ self.application.quit()
+
+
+
+
+class InteProxyMainWindow(gtk.Window):
+
+ """The mainwindow of InteProxy"""
+
+ def __init__(self, application):
+ """Initializes the main window with the application to which it belongs
+ """
+ gtk.Window.__init__(self)
+ self.application = application
+
+ self.add_accel_group(self.application.ui.get_accel_group())
+
+ self.set_position(gtk.WIN_POS_CENTER)
+ self.set_title('InteProxy')
+ self.connect('delete-event', self.delete_event)
+ self.set_size_request(400, 200)
+ vbox = gtk.VBox()
+ self.add(vbox)
+
+ vbox.pack_start(self.application.ui.get_widget('/Menubar'),
+ expand=False)
+ vbox.pack_start(gtk.Label("InteProxy Status"))
+
+ vbox.show_all()
+
+ def delete_event(self, *args):
+ """Handler for the delete-even that hides the window"""
+ self.application.hide_main_window()
+ return True
+
+
+
+class InteProxyApplication(inteproxy.app.InteProxyApplication):
+
+ """GUI version of InteProxyApplication
+
+ This InteProxyApplication shows a main window and a status icon if
+ the GTK version used implements it (requires GTK >= 2.10). If a
+ status icon is shown the main window is hidden initially.
+ """
+
+ def __init__(self, server):
+ """
+ Initializes the InteProxyApplication with the server and creates the GUI
+ """
+ super(InteProxyApplication, self).__init__(server)
+
+ self.create_ui()
+ if hasattr(gtk, "StatusIcon"):
+ self.status_icon = StatusIcon(self)
+ else:
+ self.status_icon = None
+ self.main_window = InteProxyMainWindow(self)
+
+ if not self.status_icon:
+ self.main_window.show()
+
+ def create_ui(self):
+ """Internal: Parses the ui_definition"""
+ ag = gtk.ActionGroup('WindowActions')
+ actions = [
+ ('FileMenu', None, '_File'),
+ ('Close', gtk.STOCK_CLOSE, '_Close', '<control>W',
+ 'Close the InteProxy window', self.hide_main_window),
+ ('Quit', gtk.STOCK_QUIT, '_Quit', '<control>Q',
+ 'Quit application', self.quit),
+ ('HelpMenu', None, '_Help'),
+ ('About', None, '_About', None, 'About application',
+ self.run_about_dialog),
+ ('StatusIconMenu', None, '_StatusIconMenu'),
+ ('ShowWindow', None, '_Show Window', '<control>S',
+ 'Show the InteProxy window', self.show_main_window),
+ ]
+ ag.add_actions(actions)
+ self.ui = gtk.UIManager()
+ self.ui.insert_action_group(ag, 0)
+ self.ui.add_ui_from_string(ui_definition)
+
+ def run(self):
+ """Runs the InteProxyApplication until the user quits.
+
+ The method starts a thread for the inteproxy server and then
+ enters the gtk main loop.
+ """
+ self.start_inte_proxy_thread(daemon=True)
+ gtk.gdk.threads_init()
+ gtk.main()
+
+ def quit(self, *args):
+ """Quits the inte proxy"""
+ sys.exit()
+
+ def show_main_window(self, *args):
+ """Shows the main window"""
+ self.main_window.show()
+
+ def hide_main_window(self, *args):
+ """Hides the main window if a status icon is shown other wise quits"""
+ if self.status_icon:
+ self.main_window.hide()
+ else:
+ self.quit()
+
+ def run_about_dialog(self, *args):
+ """Run the about dialog"""
+ dialog = gtk.MessageDialog(self.main_window,
+ (gtk.DIALOG_MODAL |
+ gtk.DIALOG_DESTROY_WITH_PARENT),
+ gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
+ ("InteProxy version %s"
+ % inteproxy.main.inteproxy_version))
+ dialog.run()
+ dialog.destroy()
+
+ def get_password(self, *args):
+ """Asks the user for username and password
+
+ Extends the inherited method to be thread safe in GTK, so that
+ it can be called from the server's worker threads.
+ """
+ gtk.gdk.threads_enter()
+ try:
+ return super(InteProxyApplication, self).get_password(*args)
+ finally:
+ gtk.gdk.threads_leave()
+
+ def run_fees_dialog(self, *args):
+ """Runs the fees dialog.
+
+ Extends the inherited method to be thread safe in GTK, so that
+ it can be called from the server's worker threads.
+ """
+ gtk.gdk.threads_enter()
+ try:
+ super(InteProxyApplication, self).run_fees_dialog(*args)
+ finally:
+ gtk.gdk.threads_leave()
Property changes on: trunk/inteproxy/gtkapp.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
Modified: trunk/inteproxy/httpserver.py
===================================================================
--- trunk/inteproxy/httpserver.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/httpserver.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -40,3 +40,48 @@
"""
BaseHTTPServer.HTTPServer.server_close(self)
self.do_shutdown = True
+
+
+class ServerThread(object):
+
+ """Class to run a HTTPServer instance in a thread"""
+
+ def __init__(self, server):
+ """Initializes the server thread with an instance of HTTPServer"""
+ self.server = server
+ self.server_port = self.server.getsockname()[1]
+ self.server_thread = None
+
+ def start(self, daemon=False):
+ """Starts the server thread"""
+ self.server_thread = threading.Thread(target=self._serve_forever)
+ self.server_thread.setDaemon(daemon)
+ self.server_thread.start()
+
+ def _serve_forever(self):
+ """Helper method to run the server's serve_forever method in a thread"""
+ self.server.serve_forever()
+
+ def stop(self):
+ """Stops the server thread"""
+ if self.server_thread is None:
+ return
+
+ self.server.server_close()
+
+ # The server thread might be blocked while listening on the
+ # socket. Unblock it by connecting to the port.
+ try:
+ s = socket.socket()
+ s.connect(("localhost", self.server_port))
+ s.close()
+ except socket.error, exc:
+ # The server may have shut down already when we try to
+ # connect, so we ignore connection failures.
+ if exc[0] == errno.ECONNREFUSED:
+ pass
+ else:
+ raise
+
+ self.server_thread.join()
+ self.server_thread = None
Modified: trunk/inteproxy/main.py
===================================================================
--- trunk/inteproxy/main.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/main.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -14,11 +14,11 @@
import urlparse
import urllib2
import inteproxy.proxyconnection as proxyconnection
-from inteproxy.transcoder import transcoder_map
-from inteproxy.getpassword import getpassword
+import inteproxy.transcoder
from inteproxy.proxycore import (InteProxyHTTPRequestHandler,
MasterWorkerServer,
log_date_time_string)
+from inteproxy.gtkapp import InteProxyApplication
inteproxy_version = "0.1.2"
@@ -32,7 +32,7 @@
# no password known yet. Ask the user
authuri = "https://" + "".join(self.reduce_uri(authuri))
title = "Username for %r realm %r: " % (authuri, realm)
- user, password = getpassword(title)
+ user, password = the_application.get_password(title)
return user, password
def setup_urllib2(debuglevel):
@@ -56,6 +56,11 @@
urllib2.install_opener(opener)
+# The main application object. It's a global variable so that worker
+# threads can easily get access to it.
+the_application = None
+
+
def run_server(HandlerClass = InteProxyHTTPRequestHandler,
ServerClass = MasterWorkerServer):
"""Run the InteProxy server"""
@@ -80,7 +85,7 @@
HandlerClass.allow_shutdown = opts.allow_shutdown
HandlerClass.debug_level = opts.debug_level
- transcoder_map.read_config(opts.config_file)
+ inteproxy.transcoder.transcoder_map.read_config(opts.config_file)
setup_urllib2(opts.debug_level)
@@ -94,10 +99,13 @@
httpd = ServerClass(server_address, HandlerClass, opts.workers)
+ global the_application
+ the_application = InteProxyApplication(httpd)
+
print "Serving HTTP on port", opts.port, "..."
sys.stderr.write("[%s] serving HTTP on port %d\n"
% (log_date_time_string(), opts.port))
- httpd.serve_forever()
+ the_application.run()
logmessage = "[%s] server stopped" % (log_date_time_string(),)
print logmessage
sys.stderr.write(logmessage + "\n")
Modified: trunk/inteproxy/proxycore.py
===================================================================
--- trunk/inteproxy/proxycore.py 2007-04-24 15:33:23 UTC (rev 64)
+++ trunk/inteproxy/proxycore.py 2007-04-26 19:04:26 UTC (rev 65)
@@ -15,7 +15,7 @@
import BaseHTTPServer
import socket
-from inteproxy.transcoder import transcoder_map
+import inteproxy.transcoder
from inteproxy.threadpool import ThreadPool
from inteproxy.feesdialog import handle_fees_and_access_constraints
from inteproxy.httpserver import HTTPServer
@@ -92,6 +92,7 @@
#
# Create a http request for the real location
#
+ transcoder_map = inteproxy.transcoder.transcoder_map
transcoder = transcoder_map.get_transcoder(method, self.path)
remote_url = transcoder.get_url()
self.log_debug("Converted url: %r", remote_url)
More information about the Inteproxy-commits
mailing list