Query server for list of builders and display list in main window.
authorTim Potter <tpot@hp.com>
Mon, 7 Apr 2008 01:32:08 +0000 (11:32 +1000)
committerTim Potter <tpot@hp.com>
Mon, 7 Apr 2008 01:32:08 +0000 (11:32 +1000)
gbuildbotclient
gbuildbotclient.glade

index 799e229..d0536c8 100755 (executable)
 #!/usr/bin/python
 
+# Imports to initialise twisted reactor and pygtk
+
 import pygtk
 pygtk.require('2.0')
 
 from twisted.internet import gtk2reactor
 gtk2reactor.install()
 
+import gnome.ui
+gnome.init('gbuildbotclient', '0.1')
+
+# Misc other imports
+
+import sys, string
+import gobject, gtk.glade
 from twisted.internet import reactor
+from twisted.spread import pb
+from twisted.cred import credentials
+from twisted.python import log
 
-import gnome.ui, gtk.glade
-gnome.init('gbuildbotclient', '0.1')
+# A class to manage the remote connection to the buildbot server
 
-class BuildbotClient:
+class BuildbotClient(pb.Referenceable):
 
     def __init__(self):
 
+        self.builders = {}
+        self.remote = None
+        
+    def callRemote(self, *args, **kwargs):
+        return self.remote.callRemote(*args, **kwargs)
+
+    def connect(self, host, port, username, password):
+
+        cf = pb.PBClientFactory()
+        creds = credentials.UsernamePassword(username, password)
+
+        d = cf.login(creds)
+        reactor.connectTCP(host, port, cf)
+
+        d.addCallback(self.connected)
+        
+        log.msg('connecting to %s:%d as %s' % (host, port, username))
+
+        return d
+
+    def connected(self, ref):
+
+        log.msg('connected')
+
+        # Remote reference is a StatusClientPerspective object.
+
+        self.remote = ref
+        self.remote.notifyOnDisconnect(self.disconnected)
+
+    def disconnected(self, ref):
+
+        log.msg('disconnected')
+
+        ref = None        
+
+# Application class
+
+class App:
+
+    COL_BUILDER = 0
+    COL_BUILDER_NAME = 1
+
+    def __init__(self, host, port, username, password):
+
+        # Initialise buildbot client
+
+        self.client = BuildbotClient()
+
+        # Initialise GUI stuff
+
         self.xml = gtk.glade.XML('gbuildbotclient.glade')
 
         self.win = self.xml.get_widget('toplevel')
         self.win.connect('destroy', gtk.main_quit)
 
+        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, str)
+        self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
+
+        view = gtk.TreeView(self.model)
+
+        view.append_column(
+            gtk.TreeViewColumn('Name', gtk.CellRendererText(), 
+                               text = self.COL_BUILDER_NAME))
+
+        view.show()
+
+        scrolledwindow = self.xml.get_widget('builders_scrolledwindow')
+        scrolledwindow.add_with_viewport(view)
+        scrolledwindow.show()
+
         self.win.show()
 
+        # Fetch list of 
+
+        d = self.client.connect(host, port, username, password)
+        d.addCallback(self._initModel)
+
+    def _initModel(self, *args):
+        
+        # Call getBuilder() on each item returned by getBuilderNames()
+        # and add (builder, name) tuple to the model.
+
+        def addBuilderAndName(builder, name):
+            iter = self.model.append()
+            self.model.set_value(iter, self.COL_BUILDER, builder)
+            self.model.set_value(iter, self.COL_BUILDER_NAME, name)
+
+        self.client.callRemote('getBuilderNames').addCallback(
+            lambda names: 
+                [self.client.callRemote('getBuilder', name).addCallback
+                 (addBuilderAndName, name) for name in names])
+
+# Main function
+
 if __name__ == '__main__':
 
-    app = BuildbotClient()
+    # Command line parsing
+
+    from optparse import OptionParser
+
+    optparser = OptionParser(
+        usage = '%prog HOSTNAME:PORT -u USER -p PASS')
+
+    optparser.add_option('-u', '--user', dest = 'user',
+                         action = 'store', type = 'string',
+                         help = 'user to connect as')
+
+    optparser.add_option('-p', '--password', dest = 'password',
+                         action = 'store', type = 'string',
+                         help = 'password to connect user as')
+
+    (opts, argv) = optparser.parse_args()
+
+    if len(argv) != 1:
+        optparser.print_usage()
+        sys.exit(1)
+
+    if opts.user is None:
+        print 'Username required'
+        optparser.print_usage()
+        sys.exit(1)
+
+    if opts.password is None:
+        print 'Password required'
+        optparser.print_usage()
+        sys.exit(1)
+
+    host, port = string.split(argv[0], ':')
+
+    # Start application
+
+    log.startLogging(sys.stdout)
+    app = App(host, int(port), opts.user, opts.password)
 
     reactor.run()
index 1677600..909b7ca 100644 (file)
@@ -1,11 +1,13 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.0 on Mon Apr  7 10:44:31 2008 -->
+<!--Generated with glade3 3.4.0 on Mon Apr  7 11:19:58 2008 -->
 <glade-interface>
   <requires lib="bonobo"/>
   <requires lib="gnome"/>
   <widget class="GnomeApp" id="toplevel">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="default_width">600</property>
+    <property name="default_height">500</property>
     <child internal-child="dock">
       <widget class="BonoboDock" id="app1-dock1">
         <child>
           </packing>
         </child>
         <child>
-          <placeholder/>
+          <widget class="GtkScrolledWindow" id="builders_scrolledwindow">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <child>
+              <placeholder/>
+            </child>
+          </widget>
         </child>
       </widget>
       <packing>