diff options
Diffstat (limited to 'buildbot/contrib/bb_applet.py')
-rwxr-xr-x | buildbot/contrib/bb_applet.py | 413 |
1 files changed, 0 insertions, 413 deletions
diff --git a/buildbot/contrib/bb_applet.py b/buildbot/contrib/bb_applet.py deleted file mode 100755 index 8430a2f..0000000 --- a/buildbot/contrib/bb_applet.py +++ /dev/null @@ -1,413 +0,0 @@ -#! /usr/bin/python - -# This is a Gnome-2 panel applet that uses the -# buildbot.status.client.PBListener interface to display a terse summary of -# the buildmaster. It displays one column per builder, with a box on top for -# the status of the most recent build (red, green, or orange), and a somewhat -# smaller box on the bottom for the current state of the builder (white for -# idle, yellow for building, red for offline). There are tooltips available -# to tell you which box is which. - -# Edit the line at the beginning of the MyApplet class to fill in the host -# and portnumber of your buildmaster's PBListener status port. Eventually -# this will move into a preferences dialog, but first we must create a -# preferences dialog. - -# See the notes at the end for installation hints and support files (you -# cannot simply run this script from the shell). You must create a bonobo -# .server file that points to this script, and put the .server file somewhere -# that bonobo will look for it. Only then will this applet appear in the -# panel's "Add Applet" menu. - -# Note: These applets are run in an environment that throws away stdout and -# stderr. Any logging must be done with syslog or explicitly to a file. -# Exceptions are particularly annoying in such an environment. - -# -Brian Warner, warner@lothar.com - -if 0: - import sys - dpipe = open("/tmp/applet.log", "a", 1) - sys.stdout = dpipe - sys.stderr = dpipe - print "starting" - -from twisted.internet import gtk2reactor -gtk2reactor.install() - -import gtk -import gnomeapplet - -# preferences are not yet implemented -MENU = """ -<popup name="button3"> - <menuitem name="Connect" verb="Connect" label="Connect" - pixtype="stock" pixname="gtk-refresh"/> - <menuitem name="Disconnect" verb="Disconnect" label="Disconnect" - pixtype="stock" pixname="gtk-stop"/> - <menuitem name="Prefs" verb="Props" label="_Preferences..." - pixtype="stock" pixname="gtk-properties"/> -</popup> -""" - -from twisted.spread import pb -from twisted.cred import credentials - -# sigh, these constants should cross the wire as strings, not integers -SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION = range(5) -Results = ["success", "warnings", "failure", "skipped", "exception"] - - -class Box: - - def __init__(self, buildername, hbox, tips, size, hslice): - self.buildername = buildername - self.hbox = hbox - self.tips = tips - self.state = "idle" - self.eta = None - self.last_results = None - self.last_text = None - self.size = size - self.hslice = hslice - - def create(self): - self.vbox = gtk.VBox(False) - l = gtk.Label(".") - self.current_box = box = gtk.EventBox() - # these size requests are somewhat non-deterministic. I think it - # depends upon how large label is, or how much space was already - # consumed when the box is added. - self.current_box.set_size_request(self.hslice, self.size * 0.75) - box.add(l) - self.vbox.pack_end(box) - self.current_box.modify_bg(gtk.STATE_NORMAL, - gtk.gdk.color_parse("gray50")) - - l2 = gtk.Label(".") - self.last_box = gtk.EventBox() - self.current_box.set_size_request(self.hslice, self.size * 0.25) - self.last_box.add(l2) - self.vbox.pack_end(self.last_box, True, True) - self.vbox.show_all() - self.hbox.pack_start(self.vbox, True, True) - - def remove(self): - self.hbox.remove(self.box) - - def set_state(self, state): - self.state = state - self.update() - - def set_eta(self, eta): - self.eta = eta - self.update() - - def set_last_build_results(self, results): - self.last_results = results - self.update() - - def set_last_build_text(self, text): - self.last_text = text - self.update() - - def update(self): - currentmap = {"offline": "red", - "idle": "white", - "waiting": "yellow", - "interlocked": "yellow", - "building": "yellow", - } - color = currentmap[self.state] - self.current_box.modify_bg(gtk.STATE_NORMAL, - gtk.gdk.color_parse(color)) - lastmap = {None: "gray50", - SUCCESS: "green", - WARNINGS: "orange", - FAILURE: "red", - EXCEPTION: "purple", - } - last_color = lastmap[self.last_results] - self.last_box.modify_bg(gtk.STATE_NORMAL, - gtk.gdk.color_parse(last_color)) - current_tip = "%s:\n%s" % (self.buildername, self.state) - if self.eta is not None: - current_tip += " (ETA=%ds)" % self.eta - self.tips.set_tip(self.current_box, current_tip) - last_tip = "%s:\n" % self.buildername - if self.last_text: - last_tip += "\n".join(self.last_text) - else: - last_tip += "no builds" - self.tips.set_tip(self.last_box, last_tip) - - -class MyApplet(pb.Referenceable): - # CHANGE THIS TO POINT TO YOUR BUILDMASTER - buildmaster = "buildmaster.example.org", 12345 - filled = None - - def __init__(self, container): - self.applet = container - self.size = container.get_size() - self.hslice = self.size / 4 - container.set_size_request(self.size, self.size) - self.fill_nut() - verbs = [("Props", self.menu_preferences), - ("Connect", self.menu_connect), - ("Disconnect", self.menu_disconnect), - ] - container.setup_menu(MENU, verbs) - self.boxes = {} - self.connect() - - def fill(self, what): - if self.filled: - self.applet.remove(self.filled) - self.filled = None - self.applet.add(what) - self.filled = what - self.applet.show_all() - - def fill_nut(self): - i = gtk.Image() - i.set_from_file("/tmp/nut32.png") - self.fill(i) - - def fill_hbox(self): - self.hbox = gtk.HBox(True) - self.fill(self.hbox) - - def connect(self): - host, port = self.buildmaster - cf = pb.PBClientFactory() - creds = credentials.UsernamePassword("statusClient", "clientpw") - d = cf.login(creds) - reactor.connectTCP(host, port, cf) - d.addCallback(self.connected) - return d - - def connected(self, ref): - print "connected" - ref.notifyOnDisconnect(self.disconnected) - self.remote = ref - self.remote.callRemote("subscribe", "steps", 5, self) - self.fill_hbox() - self.tips = gtk.Tooltips() - self.tips.enable() - - def disconnect(self): - self.remote.broker.transport.loseConnection() - - def disconnected(self, *args): - print "disconnected" - self.fill_nut() - - def remote_builderAdded(self, buildername, builder): - print "builderAdded", buildername - box = Box(buildername, self.hbox, self.tips, self.size, self.hslice) - self.boxes[buildername] = box - box.create() - self.applet.set_size_request(self.hslice * len(self.boxes), - self.size) - d = builder.callRemote("getLastFinishedBuild") - - def _got(build): - if build: - d1 = build.callRemote("getResults") - d1.addCallback(box.set_last_build_results) - d2 = build.callRemote("getText") - d2.addCallback(box.set_last_build_text) - d.addCallback(_got) - - def remote_builderRemoved(self, buildername): - self.boxes[buildername].remove() - del self.boxes[buildername] - self.applet.set_size_request(self.hslice * len(self.boxes), - self.size) - - def remote_builderChangedState(self, buildername, state, eta): - self.boxes[buildername].set_state(state) - self.boxes[buildername].set_eta(eta) - print "change", buildername, state, eta - - def remote_buildStarted(self, buildername, build): - print "buildStarted", buildername - - def remote_buildFinished(self, buildername, build, results): - print "buildFinished", results - box = self.boxes[buildername] - box.set_eta(None) - d1 = build.callRemote("getResults") - d1.addCallback(box.set_last_build_results) - d2 = build.callRemote("getText") - d2.addCallback(box.set_last_build_text) - - def remote_buildETAUpdate(self, buildername, build, eta): - self.boxes[buildername].set_eta(eta) - print "ETA", buildername, eta - - def remote_stepStarted(self, buildername, build, stepname, step): - print "stepStarted", buildername, stepname - - def remote_stepFinished(self, buildername, build, stepname, step, results): - pass - - def menu_preferences(self, event, data=None): - print "prefs!" - p = Prefs(self) - p.create() - - def set_buildmaster(self, buildmaster): - host, port = buildmaster.split(":") - self.buildmaster = host, int(port) - self.disconnect() - reactor.callLater(0.5, self.connect) - - def menu_connect(self, event, data=None): - self.connect() - - def menu_disconnect(self, event, data=None): - self.disconnect() - - -class Prefs: - - def __init__(self, parent): - self.parent = parent - - def create(self): - self.w = w = gtk.Window() - v = gtk.VBox() - h = gtk.HBox() - h.pack_start(gtk.Label("buildmaster (host:port) : ")) - self.buildmaster_entry = b = gtk.Entry() - if self.parent.buildmaster: - host, port = self.parent.buildmaster - b.set_text("%s:%d" % (host, port)) - h.pack_start(b) - v.add(h) - - b = gtk.Button("Ok") - b.connect("clicked", self.done) - v.add(b) - - w.add(v) - w.show_all() - - def done(self, widget): - buildmaster = self.buildmaster_entry.get_text() - self.parent.set_buildmaster(buildmaster) - self.w.unmap() - - -def factory(applet, iid): - MyApplet(applet) - applet.show_all() - return True - - -from twisted.internet import reactor - -# instead of reactor.run(), we do the following: -reactor.startRunning() -reactor.simulate() -gnomeapplet.bonobo_factory("OAFIID:GNOME_Buildbot_Factory", - gnomeapplet.Applet.__gtype__, - "buildbot", "0", factory) - -# code ends here: bonobo_factory runs gtk.mainloop() internally and -# doesn't return until the program ends - -# SUPPORTING FILES: - -# save the following as ~/lib/bonobo/servers/bb_applet.server, and update all -# the pathnames to match your system -bb_applet_server = """ -<oaf_info> - -<oaf_server iid="OAFIID:GNOME_Buildbot_Factory" - type="exe" - location="/home/warner/stuff/buildbot-trunk/contrib/bb_applet.py"> - - <oaf_attribute name="repo_ids" type="stringv"> - <item value="IDL:Bonobo/GenericFactory:1.0"/> - <item value="IDL:Bonobo/Unknown:1.0"/> - </oaf_attribute> - <oaf_attribute name="name" type="string" value="Buildbot Factory"/> - <oaf_attribute name="description" type="string" value="Test"/> -</oaf_server> - -<oaf_server iid="OAFIID:GNOME_Buildbot" - type="factory" - location="OAFIID:GNOME_Buildbot_Factory"> - - <oaf_attribute name="repo_ids" type="stringv"> - <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/> - <item value="IDL:Bonobo/Control:1.0"/> - <item value="IDL:Bonobo/Unknown:1.0"/> - </oaf_attribute> - <oaf_attribute name="name" type="string" value="Buildbot"/> - <oaf_attribute name="description" type="string" - value="Watch Buildbot status" - /> - <oaf_attribute name="panel:category" type="string" value="Utility"/> - <oaf_attribute name="panel:icon" type="string" - value="/home/warner/stuff/buildbot-trunk/doc/hexnut32.png" - /> - -</oaf_server> - -</oaf_info> -""" - -# a quick rundown on the Gnome2 applet scheme (probably wrong: there are -# better docs out there that you should be following instead) -# http://www.pycage.de/howto_bonobo.html describes a lot of -# the base Bonobo stuff. -# http://www.daa.com.au/pipermail/pygtk/2002-September/003393.html - -# bb_applet.server must be in your $BONOBO_ACTIVATION_PATH . I use -# ~/lib/bonobo/servers . This environment variable is read by -# bonobo-activation-server, so it must be set before you start any Gnome -# stuff. I set it in ~/.bash_profile . You can also put it in -# /usr/lib/bonobo/servers/ , which is probably on the default -# $BONOBO_ACTIVATION_PATH, so you won't have to update anything. - -# It is safest to put this in place before bonobo-activation-server is -# started, which may mean before any Gnome program is running. It may or may -# not detect bb_applet.server if it is installed afterwards.. there seem to -# be hooks, some of which involve FAM, but I never managed to make them work. -# The file must have a name that ends in .server or it will be ignored. - -# The .server file registers two OAF ids and tells the activation-server how -# to create those objects. The first is the GNOME_Buildbot_Factory, and is -# created by running the bb_applet.py script. The second is the -# GNOME_Buildbot applet itself, and is created by asking the -# GNOME_Buildbot_Factory to make it. - -# gnome-panel's "Add To Panel" menu will gather all the OAF ids that claim -# to implement the "IDL:GNOME/Vertigo/PanelAppletShell:1.0" in its -# "repo_ids" attribute. The sub-menu is determined by the "panel:category" -# attribute. The icon comes from "panel:icon", the text displayed in the -# menu comes from "name", the text in the tool-tip comes from "description". - -# The factory() function is called when a new applet is created. It receives -# a container that should be populated with the actual applet contents (in -# this case a Button). - -# If you're hacking on the code, just modify bb_applet.py and then kill -9 -# the running applet: the panel will ask you if you'd like to re-load the -# applet, and when you say 'yes', bb_applet.py will be re-executed. Note that -# 'kill PID' won't work because the program is sitting in C code, and SIGINT -# isn't delivered until after it surfaces to python, which will be never. - -# Running bb_applet.py by itself will result in a factory instance being -# created and then sitting around forever waiting for the activation-server -# to ask it to make an applet. This isn't very useful. - -# The "location" filename in bb_applet.server must point to bb_applet.py, and -# bb_applet.py must be executable. - -# Enjoy! -# -Brian Warner |